blob: 393dbbca4e85c13c32eed9264b605da7f9abbb3c [file] [log] [blame]
Luming Yu79cda7d2005-08-03 18:07:59 -04001/*
2 * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $)
Luming Yufb9802f2005-03-18 18:03:45 -05003 *
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
Luming Yu79cda7d2005-08-03 18:07:59 -040054#define RESULT_STR_LEN 80
Luming Yufb9802f2005-03-18 18:03:45 -050055
Luming Yu79cda7d2005-08-03 18:07:59 -040056#define ACTION_METHOD 0
57#define POLL_METHOD 1
Luming Yufb9802f2005-03-18 18:03:45 -050058
Luming Yu79cda7d2005-08-03 18:07:59 -040059#define IS_EVENT(e) ((e) <= 10000 && (e) >0)
60#define IS_POLL(e) ((e) > 10000)
61#define IS_OTHERS(e) ((e)<=0 || (e)>=20000)
Luming Yufb9802f2005-03-18 18:03:45 -050062#define _COMPONENT ACPI_HOTKEY_COMPONENT
63ACPI_MODULE_NAME("acpi_hotkey")
64
Len Brown4be44fc2005-08-05 00:44:28 -040065 MODULE_AUTHOR("luming.yu@intel.com");
Luming Yufb9802f2005-03-18 18:03:45 -050066MODULE_DESCRIPTION(ACPI_HOTK_NAME);
67MODULE_LICENSE("GPL");
68
69/* standardized internal hotkey number/event */
70enum {
71 /* Video Extension event */
72 HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80,
73 HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE,
74 HK_EVENT_CYCLE_DISPLAY_OUTPUT,
75 HK_EVENT_NEXT_DISPLAY_OUTPUT,
76 HK_EVENT_PREVIOUS_DISPLAY_OUTPUT,
77 HK_EVENT_CYCLE_BRIGHTNESS,
78 HK_EVENT_INCREASE_BRIGHTNESS,
79 HK_EVENT_DECREASE_BRIGHTNESS,
80 HK_EVENT_ZERO_BRIGHTNESS,
81 HK_EVENT_DISPLAY_DEVICE_OFF,
82
83 /* Snd Card event */
84 HK_EVENT_VOLUME_MUTE,
85 HK_EVENT_VOLUME_INCLREASE,
86 HK_EVENT_VOLUME_DECREASE,
87
88 /* running state control */
89 HK_EVENT_ENTERRING_S3,
90 HK_EVENT_ENTERRING_S4,
91 HK_EVENT_ENTERRING_S5,
92};
93
94/* procdir we use */
95static struct proc_dir_entry *hotkey_proc_dir;
96static struct proc_dir_entry *hotkey_config;
97static struct proc_dir_entry *hotkey_poll_config;
98static struct proc_dir_entry *hotkey_action;
99static struct proc_dir_entry *hotkey_info;
100
101/* linkage for all type of hotkey */
102struct acpi_hotkey_link {
103 struct list_head entries;
104 int hotkey_type; /* event or polling based hotkey */
105 int hotkey_standard_num; /* standardized hotkey(event) number */
106};
107
108/* event based hotkey */
109struct acpi_event_hotkey {
110 struct acpi_hotkey_link hotkey_link;
111 int flag;
112 acpi_handle bus_handle; /* bus to install notify handler */
113 int external_hotkey_num; /* external hotkey/event number */
114 acpi_handle action_handle; /* acpi handle attached aml action method */
115 char *action_method; /* action method */
116};
117
Luming Yu79cda7d2005-08-03 18:07:59 -0400118/*
Luming Yufb9802f2005-03-18 18:03:45 -0500119 * There are two ways to poll status
120 * 1. directy call read_xxx method, without any arguments passed in
121 * 2. call write_xxx method, with arguments passed in, you need
122 * the result is saved in acpi_polling_hotkey.poll_result.
123 * anthoer read command through polling interface.
124 *
125 */
126
127/* polling based hotkey */
128struct acpi_polling_hotkey {
129 struct acpi_hotkey_link hotkey_link;
130 int flag;
131 acpi_handle poll_handle; /* acpi handle attached polling method */
132 char *poll_method; /* poll method */
133 acpi_handle action_handle; /* acpi handle attached action method */
134 char *action_method; /* action method */
Luming Yu79cda7d2005-08-03 18:07:59 -0400135 union acpi_object *poll_result; /* polling_result */
Luming Yufb9802f2005-03-18 18:03:45 -0500136 struct proc_dir_entry *proc;
137};
138
139/* hotkey object union */
140union acpi_hotkey {
141 struct list_head entries;
142 struct acpi_hotkey_link link;
143 struct acpi_event_hotkey event_hotkey;
144 struct acpi_polling_hotkey poll_hotkey;
145};
146
147/* hotkey object list */
148struct acpi_hotkey_list {
149 struct list_head *entries;
150 int count;
151};
152
153static int auto_hotkey_add(struct acpi_device *device);
154static int auto_hotkey_remove(struct acpi_device *device, int type);
155
156static struct acpi_driver hotkey_driver = {
157 .name = ACPI_HOTK_NAME,
158 .class = ACPI_HOTK_CLASS,
159 .ids = ACPI_HOTK_HID,
160 .ops = {
161 .add = auto_hotkey_add,
162 .remove = auto_hotkey_remove,
163 },
164};
165
Luming Yu79cda7d2005-08-03 18:07:59 -0400166static void free_hotkey_device(union acpi_hotkey *key);
167static void free_hotkey_buffer(union acpi_hotkey *key);
168static void free_poll_hotkey_buffer(union acpi_hotkey *key);
Luming Yufb9802f2005-03-18 18:03:45 -0500169static int hotkey_open_config(struct inode *inode, struct file *file);
Luming Yu79cda7d2005-08-03 18:07:59 -0400170static int hotkey_poll_open_config(struct inode *inode, struct file *file);
Luming Yufb9802f2005-03-18 18:03:45 -0500171static ssize_t hotkey_write_config(struct file *file,
172 const char __user * buffer,
173 size_t count, loff_t * data);
Luming Yufb9802f2005-03-18 18:03:45 -0500174static int hotkey_info_open_fs(struct inode *inode, struct file *file);
175static int hotkey_action_open_fs(struct inode *inode, struct file *file);
176static ssize_t hotkey_execute_aml_method(struct file *file,
177 const char __user * buffer,
178 size_t count, loff_t * data);
179static int hotkey_config_seq_show(struct seq_file *seq, void *offset);
Luming Yu79cda7d2005-08-03 18:07:59 -0400180static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset);
Luming Yufb9802f2005-03-18 18:03:45 -0500181static int hotkey_polling_open_fs(struct inode *inode, struct file *file);
Luming Yu79cda7d2005-08-03 18:07:59 -0400182static union acpi_hotkey *get_hotkey_by_event(struct
Len Brown4be44fc2005-08-05 00:44:28 -0400183 acpi_hotkey_list
184 *hotkey_list, int event);
Luming Yufb9802f2005-03-18 18:03:45 -0500185
186/* event based config */
187static struct file_operations hotkey_config_fops = {
188 .open = hotkey_open_config,
189 .read = seq_read,
190 .write = hotkey_write_config,
191 .llseek = seq_lseek,
192 .release = single_release,
193};
194
195/* polling based config */
196static struct file_operations hotkey_poll_config_fops = {
Luming Yu79cda7d2005-08-03 18:07:59 -0400197 .open = hotkey_poll_open_config,
Luming Yufb9802f2005-03-18 18:03:45 -0500198 .read = seq_read,
Luming Yu79cda7d2005-08-03 18:07:59 -0400199 .write = hotkey_write_config,
Luming Yufb9802f2005-03-18 18:03:45 -0500200 .llseek = seq_lseek,
201 .release = single_release,
202};
203
204/* hotkey driver info */
205static struct file_operations hotkey_info_fops = {
206 .open = hotkey_info_open_fs,
207 .read = seq_read,
208 .llseek = seq_lseek,
209 .release = single_release,
210};
211
212/* action */
213static struct file_operations hotkey_action_fops = {
214 .open = hotkey_action_open_fs,
215 .read = seq_read,
216 .write = hotkey_execute_aml_method,
217 .llseek = seq_lseek,
218 .release = single_release,
219};
220
221/* polling results */
222static struct file_operations hotkey_polling_fops = {
223 .open = hotkey_polling_open_fs,
224 .read = seq_read,
225 .llseek = seq_lseek,
226 .release = single_release,
227};
228
229struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */
230struct list_head hotkey_entries; /* head of the list of hotkey_list */
231
232static int hotkey_info_seq_show(struct seq_file *seq, void *offset)
233{
234 ACPI_FUNCTION_TRACE("hotkey_info_seq_show");
235
Luming Yu79cda7d2005-08-03 18:07:59 -0400236 seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION);
Luming Yufb9802f2005-03-18 18:03:45 -0500237
238 return_VALUE(0);
239}
240
241static int hotkey_info_open_fs(struct inode *inode, struct file *file)
242{
243 return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
244}
245
246static char *format_result(union acpi_object *object)
247{
Luming Yu79cda7d2005-08-03 18:07:59 -0400248 char *buf = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400249
Luming Yu79cda7d2005-08-03 18:07:59 -0400250 buf = (char *)kmalloc(RESULT_STR_LEN, GFP_KERNEL);
251 if (buf)
252 memset(buf, 0, RESULT_STR_LEN);
253 else
254 goto do_fail;
Luming Yufb9802f2005-03-18 18:03:45 -0500255
256 /* Now, just support integer type */
257 if (object->type == ACPI_TYPE_INTEGER)
Luming Yu79cda7d2005-08-03 18:07:59 -0400258 sprintf(buf, "%d\n", (u32) object->integer.value);
Len Brown4be44fc2005-08-05 00:44:28 -0400259 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400260 return (buf);
Luming Yufb9802f2005-03-18 18:03:45 -0500261}
262
263static int hotkey_polling_seq_show(struct seq_file *seq, void *offset)
264{
265 struct acpi_polling_hotkey *poll_hotkey =
266 (struct acpi_polling_hotkey *)seq->private;
Luming Yu79cda7d2005-08-03 18:07:59 -0400267 char *buf;
Luming Yufb9802f2005-03-18 18:03:45 -0500268
269 ACPI_FUNCTION_TRACE("hotkey_polling_seq_show");
270
Len Brown4be44fc2005-08-05 00:44:28 -0400271 if (poll_hotkey->poll_result) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400272 buf = format_result(poll_hotkey->poll_result);
Len Brown4be44fc2005-08-05 00:44:28 -0400273 if (buf)
Luming Yu79cda7d2005-08-03 18:07:59 -0400274 seq_printf(seq, "%s", buf);
275 kfree(buf);
276 }
Luming Yufb9802f2005-03-18 18:03:45 -0500277 return_VALUE(0);
278}
279
280static int hotkey_polling_open_fs(struct inode *inode, struct file *file)
281{
282 return single_open(file, hotkey_polling_seq_show, PDE(inode)->data);
283}
284
285static int hotkey_action_open_fs(struct inode *inode, struct file *file)
286{
287 return single_open(file, hotkey_info_seq_show, PDE(inode)->data);
288}
289
290/* Mapping external hotkey number to standardized hotkey event num */
291static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list)
292{
Luming Yu79cda7d2005-08-03 18:07:59 -0400293 struct list_head *entries;
294 int val = -1;
Luming Yufb9802f2005-03-18 18:03:45 -0500295
296 ACPI_FUNCTION_TRACE("hotkey_get_internal_event");
297
Luming Yu79cda7d2005-08-03 18:07:59 -0400298 list_for_each(entries, list->entries) {
Luming Yufb9802f2005-03-18 18:03:45 -0500299 union acpi_hotkey *key =
300 container_of(entries, union acpi_hotkey, entries);
301 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT
Len Brown4be44fc2005-08-05 00:44:28 -0400302 && key->event_hotkey.external_hotkey_num == event) {
Luming Yufb9802f2005-03-18 18:03:45 -0500303 val = key->link.hotkey_standard_num;
Luming Yu79cda7d2005-08-03 18:07:59 -0400304 break;
305 }
Luming Yufb9802f2005-03-18 18:03:45 -0500306 }
307
308 return_VALUE(val);
309}
310
311static void
312acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data)
313{
314 struct acpi_device *device = NULL;
315 u32 internal_event;
316
317 ACPI_FUNCTION_TRACE("acpi_hotkey_notify_handler");
318
319 if (acpi_bus_get_device(handle, &device))
320 return_VOID;
321
322 internal_event = hotkey_get_internal_event(event, &global_hotkey_list);
Luming Yu79cda7d2005-08-03 18:07:59 -0400323 acpi_bus_generate_event(device, internal_event, 0);
Luming Yufb9802f2005-03-18 18:03:45 -0500324
325 return_VOID;
326}
327
328/* Need to invent automatically hotkey add method */
329static int auto_hotkey_add(struct acpi_device *device)
330{
331 /* Implement me */
332 return 0;
333}
334
335/* Need to invent automatically hotkey remove method */
336static int auto_hotkey_remove(struct acpi_device *device, int type)
337{
338 /* Implement me */
339 return 0;
340}
341
342/* Create a proc file for each polling method */
343static int create_polling_proc(union acpi_hotkey *device)
344{
345 struct proc_dir_entry *proc;
Len Brown4be44fc2005-08-05 00:44:28 -0400346 char proc_name[80];
Andrew Morton8de7a632005-03-30 22:53:30 -0500347 mode_t mode;
Luming Yufb9802f2005-03-18 18:03:45 -0500348
349 ACPI_FUNCTION_TRACE("create_polling_proc");
Andrew Morton8de7a632005-03-30 22:53:30 -0500350 mode = S_IFREG | S_IRUGO | S_IWUGO;
Luming Yufb9802f2005-03-18 18:03:45 -0500351
Luming Yu79cda7d2005-08-03 18:07:59 -0400352 sprintf(proc_name, "%d", device->link.hotkey_standard_num);
353 /*
Len Brown4be44fc2005-08-05 00:44:28 -0400354 strcat(proc_name, device->poll_hotkey.poll_method);
355 */
Luming Yu79cda7d2005-08-03 18:07:59 -0400356 proc = create_proc_entry(proc_name, mode, hotkey_proc_dir);
Luming Yufb9802f2005-03-18 18:03:45 -0500357
358 if (!proc) {
Luming Yufb9802f2005-03-18 18:03:45 -0500359 return_VALUE(-ENODEV);
360 } else {
361 proc->proc_fops = &hotkey_polling_fops;
362 proc->owner = THIS_MODULE;
363 proc->data = device;
364 proc->uid = 0;
365 proc->gid = 0;
366 device->poll_hotkey.proc = proc;
367 }
368 return_VALUE(0);
369}
370
Luming Yufb9802f2005-03-18 18:03:45 -0500371static int hotkey_add(union acpi_hotkey *device)
372{
373 int status = 0;
374 struct acpi_device *dev = NULL;
375
376 ACPI_FUNCTION_TRACE("hotkey_add");
377
378 if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400379 acpi_bus_get_device(device->event_hotkey.bus_handle, &dev);
Luming Yufb9802f2005-03-18 18:03:45 -0500380 status = acpi_install_notify_handler(dev->handle,
Luming Yu79cda7d2005-08-03 18:07:59 -0400381 ACPI_DEVICE_NOTIFY,
Luming Yufb9802f2005-03-18 18:03:45 -0500382 acpi_hotkey_notify_handler,
Luming Yu79cda7d2005-08-03 18:07:59 -0400383 dev);
Luming Yufb9802f2005-03-18 18:03:45 -0500384 } else /* Add polling hotkey */
385 create_polling_proc(device);
386
387 global_hotkey_list.count++;
388
389 list_add_tail(&device->link.entries, global_hotkey_list.entries);
390
391 return_VALUE(status);
392}
393
394static int hotkey_remove(union acpi_hotkey *device)
395{
396 struct list_head *entries, *next;
397
398 ACPI_FUNCTION_TRACE("hotkey_remove");
399
400 list_for_each_safe(entries, next, global_hotkey_list.entries) {
401 union acpi_hotkey *key =
402 container_of(entries, union acpi_hotkey, entries);
403 if (key->link.hotkey_standard_num ==
404 device->link.hotkey_standard_num) {
405 list_del(&key->link.entries);
Luming Yu79cda7d2005-08-03 18:07:59 -0400406 free_hotkey_device(key);
Luming Yufb9802f2005-03-18 18:03:45 -0500407 global_hotkey_list.count--;
408 break;
409 }
410 }
Luming Yu79cda7d2005-08-03 18:07:59 -0400411 kfree(device);
Luming Yufb9802f2005-03-18 18:03:45 -0500412 return_VALUE(0);
413}
414
Len Brown4be44fc2005-08-05 00:44:28 -0400415static int hotkey_update(union acpi_hotkey *key)
Luming Yufb9802f2005-03-18 18:03:45 -0500416{
Luming Yu79cda7d2005-08-03 18:07:59 -0400417 struct list_head *entries;
Luming Yufb9802f2005-03-18 18:03:45 -0500418
419 ACPI_FUNCTION_TRACE("hotkey_update");
420
Luming Yu79cda7d2005-08-03 18:07:59 -0400421 list_for_each(entries, global_hotkey_list.entries) {
Len Brown4be44fc2005-08-05 00:44:28 -0400422 union acpi_hotkey *tmp =
Luming Yufb9802f2005-03-18 18:03:45 -0500423 container_of(entries, union acpi_hotkey, entries);
Luming Yu79cda7d2005-08-03 18:07:59 -0400424 if (tmp->link.hotkey_standard_num ==
Luming Yufb9802f2005-03-18 18:03:45 -0500425 key->link.hotkey_standard_num) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400426 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) {
427 free_hotkey_buffer(tmp);
428 tmp->event_hotkey.bus_handle =
Len Brown4be44fc2005-08-05 00:44:28 -0400429 key->event_hotkey.bus_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400430 tmp->event_hotkey.external_hotkey_num =
Len Brown4be44fc2005-08-05 00:44:28 -0400431 key->event_hotkey.external_hotkey_num;
Luming Yu79cda7d2005-08-03 18:07:59 -0400432 tmp->event_hotkey.action_handle =
Len Brown4be44fc2005-08-05 00:44:28 -0400433 key->event_hotkey.action_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400434 tmp->event_hotkey.action_method =
Len Brown4be44fc2005-08-05 00:44:28 -0400435 key->event_hotkey.action_method;
Luming Yu79cda7d2005-08-03 18:07:59 -0400436 kfree(key);
437 } else {
438 /*
Len Brown4be44fc2005-08-05 00:44:28 -0400439 char proc_name[80];
Luming Yu79cda7d2005-08-03 18:07:59 -0400440
Len Brown4be44fc2005-08-05 00:44:28 -0400441 sprintf(proc_name, "%d", tmp->link.hotkey_standard_num);
442 strcat(proc_name, tmp->poll_hotkey.poll_method);
443 remove_proc_entry(proc_name,hotkey_proc_dir);
444 */
Luming Yu79cda7d2005-08-03 18:07:59 -0400445 free_poll_hotkey_buffer(tmp);
446 tmp->poll_hotkey.poll_handle =
Len Brown4be44fc2005-08-05 00:44:28 -0400447 key->poll_hotkey.poll_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400448 tmp->poll_hotkey.poll_method =
Len Brown4be44fc2005-08-05 00:44:28 -0400449 key->poll_hotkey.poll_method;
Luming Yu79cda7d2005-08-03 18:07:59 -0400450 tmp->poll_hotkey.action_handle =
Len Brown4be44fc2005-08-05 00:44:28 -0400451 key->poll_hotkey.action_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400452 tmp->poll_hotkey.action_method =
Len Brown4be44fc2005-08-05 00:44:28 -0400453 key->poll_hotkey.action_method;
Luming Yu79cda7d2005-08-03 18:07:59 -0400454 tmp->poll_hotkey.poll_result =
Len Brown4be44fc2005-08-05 00:44:28 -0400455 key->poll_hotkey.poll_result;
Luming Yu79cda7d2005-08-03 18:07:59 -0400456 /*
Len Brown4be44fc2005-08-05 00:44:28 -0400457 create_polling_proc(tmp);
458 */
Luming Yu79cda7d2005-08-03 18:07:59 -0400459 kfree(key);
460 }
461 return_VALUE(0);
Luming Yufb9802f2005-03-18 18:03:45 -0500462 break;
463 }
464 }
465
Luming Yu79cda7d2005-08-03 18:07:59 -0400466 return_VALUE(-ENODEV);
Luming Yufb9802f2005-03-18 18:03:45 -0500467}
468
469static void free_hotkey_device(union acpi_hotkey *key)
470{
471 struct acpi_device *dev;
Luming Yufb9802f2005-03-18 18:03:45 -0500472
473 ACPI_FUNCTION_TRACE("free_hotkey_device");
474
475 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400476 acpi_bus_get_device(key->event_hotkey.bus_handle, &dev);
Luming Yufb9802f2005-03-18 18:03:45 -0500477 if (dev->handle)
478 acpi_remove_notify_handler(dev->handle,
Luming Yu79cda7d2005-08-03 18:07:59 -0400479 ACPI_DEVICE_NOTIFY,
Luming Yufb9802f2005-03-18 18:03:45 -0500480 acpi_hotkey_notify_handler);
Luming Yu79cda7d2005-08-03 18:07:59 -0400481 free_hotkey_buffer(key);
482 } else {
Len Brown4be44fc2005-08-05 00:44:28 -0400483 char proc_name[80];
Luming Yu79cda7d2005-08-03 18:07:59 -0400484
485 sprintf(proc_name, "%d", key->link.hotkey_standard_num);
486 /*
Len Brown4be44fc2005-08-05 00:44:28 -0400487 strcat(proc_name, key->poll_hotkey.poll_method);
488 */
489 remove_proc_entry(proc_name, hotkey_proc_dir);
Luming Yu79cda7d2005-08-03 18:07:59 -0400490 free_poll_hotkey_buffer(key);
491 }
Luming Yufb9802f2005-03-18 18:03:45 -0500492 kfree(key);
493 return_VOID;
494}
495
Len Brown4be44fc2005-08-05 00:44:28 -0400496static void free_hotkey_buffer(union acpi_hotkey *key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400497{
498 kfree(key->event_hotkey.action_method);
499}
500
Len Brown4be44fc2005-08-05 00:44:28 -0400501static void free_poll_hotkey_buffer(union acpi_hotkey *key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400502{
503 kfree(key->poll_hotkey.action_method);
504 kfree(key->poll_hotkey.poll_method);
505 kfree(key->poll_hotkey.poll_result);
506}
Luming Yufb9802f2005-03-18 18:03:45 -0500507static int
508init_hotkey_device(union acpi_hotkey *key, char *bus_str, char *action_str,
509 char *method, int std_num, int external_num)
510{
Len Brown4be44fc2005-08-05 00:44:28 -0400511 acpi_handle tmp_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400512 acpi_status status = AE_OK;
513
Luming Yufb9802f2005-03-18 18:03:45 -0500514 ACPI_FUNCTION_TRACE("init_hotkey_device");
515
Len Brown4be44fc2005-08-05 00:44:28 -0400516 if (std_num < 0 || IS_POLL(std_num) || !key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400517 goto do_fail;
518
Len Brown4be44fc2005-08-05 00:44:28 -0400519 if (!bus_str || !action_str || !method)
Luming Yu79cda7d2005-08-03 18:07:59 -0400520 goto do_fail;
521
Luming Yufb9802f2005-03-18 18:03:45 -0500522 key->link.hotkey_type = ACPI_HOTKEY_EVENT;
523 key->link.hotkey_standard_num = std_num;
524 key->event_hotkey.flag = 0;
Luming Yu79cda7d2005-08-03 18:07:59 -0400525 key->event_hotkey.action_method = method;
Luming Yufb9802f2005-03-18 18:03:45 -0500526
Len Brown4be44fc2005-08-05 00:44:28 -0400527 status =
528 acpi_get_handle(NULL, bus_str, &(key->event_hotkey.bus_handle));
529 if (ACPI_FAILURE(status))
Luming Yu79cda7d2005-08-03 18:07:59 -0400530 goto do_fail;
531 key->event_hotkey.external_hotkey_num = external_num;
Len Brown4be44fc2005-08-05 00:44:28 -0400532 status =
533 acpi_get_handle(NULL, action_str,
534 &(key->event_hotkey.action_handle));
535 if (ACPI_FAILURE(status))
Luming Yu79cda7d2005-08-03 18:07:59 -0400536 goto do_fail;
537 status = acpi_get_handle(key->event_hotkey.action_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400538 method, &tmp_handle);
Luming Yu79cda7d2005-08-03 18:07:59 -0400539 if (ACPI_FAILURE(status))
540 goto do_fail;
541 return_VALUE(AE_OK);
Len Brown4be44fc2005-08-05 00:44:28 -0400542 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400543 return_VALUE(-ENODEV);
Luming Yufb9802f2005-03-18 18:03:45 -0500544}
545
546static int
547init_poll_hotkey_device(union acpi_hotkey *key,
548 char *poll_str,
549 char *poll_method,
550 char *action_str, char *action_method, int std_num)
551{
Luming Yu79cda7d2005-08-03 18:07:59 -0400552 acpi_status status = AE_OK;
Len Brown4be44fc2005-08-05 00:44:28 -0400553 acpi_handle tmp_handle;
Luming Yu79cda7d2005-08-03 18:07:59 -0400554
Luming Yufb9802f2005-03-18 18:03:45 -0500555 ACPI_FUNCTION_TRACE("init_poll_hotkey_device");
556
Len Brown4be44fc2005-08-05 00:44:28 -0400557 if (std_num < 0 || IS_EVENT(std_num) || !key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400558 goto do_fail;
559
Len Brown4be44fc2005-08-05 00:44:28 -0400560 if (!poll_str || !poll_method || !action_str || !action_method)
Luming Yu79cda7d2005-08-03 18:07:59 -0400561 goto do_fail;
562
Luming Yufb9802f2005-03-18 18:03:45 -0500563 key->link.hotkey_type = ACPI_HOTKEY_POLLING;
564 key->link.hotkey_standard_num = std_num;
565 key->poll_hotkey.flag = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500566 key->poll_hotkey.poll_method = poll_method;
Luming Yu79cda7d2005-08-03 18:07:59 -0400567 key->poll_hotkey.action_method = action_method;
568
Len Brown4be44fc2005-08-05 00:44:28 -0400569 status =
570 acpi_get_handle(NULL, poll_str, &(key->poll_hotkey.poll_handle));
571 if (ACPI_FAILURE(status))
Luming Yu79cda7d2005-08-03 18:07:59 -0400572 goto do_fail;
573 status = acpi_get_handle(key->poll_hotkey.poll_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400574 poll_method, &tmp_handle);
575 if (ACPI_FAILURE(status))
576 goto do_fail;
577 status =
578 acpi_get_handle(NULL, action_str,
579 &(key->poll_hotkey.action_handle));
Luming Yu79cda7d2005-08-03 18:07:59 -0400580 if (ACPI_FAILURE(status))
581 goto do_fail;
582 status = acpi_get_handle(key->poll_hotkey.action_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400583 action_method, &tmp_handle);
Luming Yu79cda7d2005-08-03 18:07:59 -0400584 if (ACPI_FAILURE(status))
585 goto do_fail;
Luming Yufb9802f2005-03-18 18:03:45 -0500586 key->poll_hotkey.poll_result =
587 (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL);
Len Brown4be44fc2005-08-05 00:44:28 -0400588 if (!key->poll_hotkey.poll_result)
Luming Yu79cda7d2005-08-03 18:07:59 -0400589 goto do_fail;
590 return_VALUE(AE_OK);
Len Brown4be44fc2005-08-05 00:44:28 -0400591 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400592 return_VALUE(-ENODEV);
Luming Yufb9802f2005-03-18 18:03:45 -0500593}
594
Luming Yufb9802f2005-03-18 18:03:45 -0500595static int hotkey_open_config(struct inode *inode, struct file *file)
596{
597 ACPI_FUNCTION_TRACE("hotkey_open_config");
598 return_VALUE(single_open
599 (file, hotkey_config_seq_show, PDE(inode)->data));
600}
601
Luming Yu79cda7d2005-08-03 18:07:59 -0400602static int hotkey_poll_open_config(struct inode *inode, struct file *file)
603{
604 ACPI_FUNCTION_TRACE("hotkey_poll_open_config");
605 return_VALUE(single_open
606 (file, hotkey_poll_config_seq_show, PDE(inode)->data));
607}
608
Luming Yufb9802f2005-03-18 18:03:45 -0500609static int hotkey_config_seq_show(struct seq_file *seq, void *offset)
610{
611 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
Luming Yu79cda7d2005-08-03 18:07:59 -0400612 struct list_head *entries;
Luming Yufb9802f2005-03-18 18:03:45 -0500613 char bus_name[ACPI_PATHNAME_MAX] = { 0 };
614 char action_name[ACPI_PATHNAME_MAX] = { 0 };
615 struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name };
616 struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name };
617
618 ACPI_FUNCTION_TRACE(("hotkey_config_seq_show"));
619
Luming Yu79cda7d2005-08-03 18:07:59 -0400620 list_for_each(entries, hotkey_list->entries) {
Luming Yufb9802f2005-03-18 18:03:45 -0500621 union acpi_hotkey *key =
622 container_of(entries, union acpi_hotkey, entries);
623 if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) {
624 acpi_get_name(key->event_hotkey.bus_handle,
625 ACPI_NAME_TYPE_MAX, &bus);
626 acpi_get_name(key->event_hotkey.action_handle,
627 ACPI_NAME_TYPE_MAX, &act);
Luming Yu79cda7d2005-08-03 18:07:59 -0400628 seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name,
Luming Yufb9802f2005-03-18 18:03:45 -0500629 action_name,
630 key->event_hotkey.action_method,
631 key->link.hotkey_standard_num,
632 key->event_hotkey.external_hotkey_num);
Luming Yu79cda7d2005-08-03 18:07:59 -0400633 }
634 }
635 seq_puts(seq, "\n");
636 return_VALUE(0);
637}
638
639static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset)
640{
641 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
642 struct list_head *entries;
643 char bus_name[ACPI_PATHNAME_MAX] = { 0 };
644 char action_name[ACPI_PATHNAME_MAX] = { 0 };
645 struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name };
646 struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name };
647
648 ACPI_FUNCTION_TRACE(("hotkey_config_seq_show"));
649
650 list_for_each(entries, hotkey_list->entries) {
651 union acpi_hotkey *key =
652 container_of(entries, union acpi_hotkey, entries);
653 if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) {
Luming Yufb9802f2005-03-18 18:03:45 -0500654 acpi_get_name(key->poll_hotkey.poll_handle,
655 ACPI_NAME_TYPE_MAX, &bus);
656 acpi_get_name(key->poll_hotkey.action_handle,
657 ACPI_NAME_TYPE_MAX, &act);
Luming Yu79cda7d2005-08-03 18:07:59 -0400658 seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name,
Luming Yufb9802f2005-03-18 18:03:45 -0500659 key->poll_hotkey.poll_method,
660 action_name,
661 key->poll_hotkey.action_method,
662 key->link.hotkey_standard_num);
663 }
664 }
665 seq_puts(seq, "\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500666 return_VALUE(0);
667}
668
669static int
670get_parms(char *config_record,
671 int *cmd,
Luming Yu79cda7d2005-08-03 18:07:59 -0400672 char **bus_handle,
673 char **bus_method,
674 char **action_handle,
675 char **method, int *internal_event_num, int *external_event_num)
Luming Yufb9802f2005-03-18 18:03:45 -0500676{
Luming Yu79cda7d2005-08-03 18:07:59 -0400677 char *tmp, *tmp1, count;
Luming Yufb9802f2005-03-18 18:03:45 -0500678 ACPI_FUNCTION_TRACE(("get_parms"));
679
680 sscanf(config_record, "%d", cmd);
681
Len Brown4be44fc2005-08-05 00:44:28 -0400682 if (*cmd == 1) {
683 if (sscanf(config_record, "%d:%d", cmd, internal_event_num) !=
684 2)
Luming Yu79cda7d2005-08-03 18:07:59 -0400685 goto do_fail;
686 else
687 return (6);
688 }
Luming Yufb9802f2005-03-18 18:03:45 -0500689 tmp = strchr(config_record, ':');
Luming Yu79cda7d2005-08-03 18:07:59 -0400690 if (!tmp)
691 goto do_fail;
Luming Yufb9802f2005-03-18 18:03:45 -0500692 tmp++;
693 tmp1 = strchr(tmp, ':');
Luming Yu79cda7d2005-08-03 18:07:59 -0400694 if (!tmp1)
695 goto do_fail;
696
697 count = tmp1 - tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400698 *bus_handle = (char *)kmalloc(count + 1, GFP_KERNEL);
699 if (!*bus_handle)
Luming Yu79cda7d2005-08-03 18:07:59 -0400700 goto do_fail;
701 strncpy(*bus_handle, tmp, count);
702 *(*bus_handle + count) = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500703
704 tmp = tmp1;
705 tmp++;
706 tmp1 = strchr(tmp, ':');
Luming Yu79cda7d2005-08-03 18:07:59 -0400707 if (!tmp1)
708 goto do_fail;
709 count = tmp1 - tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400710 *bus_method = (char *)kmalloc(count + 1, GFP_KERNEL);
711 if (!*bus_method)
Luming Yu79cda7d2005-08-03 18:07:59 -0400712 goto do_fail;
713 strncpy(*bus_method, tmp, count);
714 *(*bus_method + count) = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500715
716 tmp = tmp1;
717 tmp++;
718 tmp1 = strchr(tmp, ':');
Luming Yu79cda7d2005-08-03 18:07:59 -0400719 if (!tmp1)
720 goto do_fail;
721 count = tmp1 - tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400722 *action_handle = (char *)kmalloc(count + 1, GFP_KERNEL);
Irwan Djajadi1fee9402006-01-20 15:28:00 -0500723 if (!*action_handle)
724 goto do_fail;
Luming Yu79cda7d2005-08-03 18:07:59 -0400725 strncpy(*action_handle, tmp, count);
726 *(*action_handle + count) = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500727
728 tmp = tmp1;
729 tmp++;
730 tmp1 = strchr(tmp, ':');
Luming Yu79cda7d2005-08-03 18:07:59 -0400731 if (!tmp1)
732 goto do_fail;
733 count = tmp1 - tmp;
Len Brown4be44fc2005-08-05 00:44:28 -0400734 *method = (char *)kmalloc(count + 1, GFP_KERNEL);
735 if (!*method)
Luming Yu79cda7d2005-08-03 18:07:59 -0400736 goto do_fail;
737 strncpy(*method, tmp, count);
738 *(*method + count) = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500739
Len Brown4be44fc2005-08-05 00:44:28 -0400740 if (sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num) <=
741 0)
Luming Yu79cda7d2005-08-03 18:07:59 -0400742 goto do_fail;
743
Luming Yufb9802f2005-03-18 18:03:45 -0500744 return_VALUE(6);
Len Brown4be44fc2005-08-05 00:44:28 -0400745 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400746 return_VALUE(-1);
Luming Yufb9802f2005-03-18 18:03:45 -0500747}
748
749/* count is length for one input record */
750static ssize_t hotkey_write_config(struct file *file,
751 const char __user * buffer,
752 size_t count, loff_t * data)
753{
Luming Yu79cda7d2005-08-03 18:07:59 -0400754 char *config_record = NULL;
755 char *bus_handle = NULL;
756 char *bus_method = NULL;
757 char *action_handle = NULL;
758 char *method = NULL;
Luming Yufb9802f2005-03-18 18:03:45 -0500759 int cmd, internal_event_num, external_event_num;
760 int ret = 0;
761 union acpi_hotkey *key = NULL;
762
763 ACPI_FUNCTION_TRACE(("hotkey_write_config"));
764
Len Brown4be44fc2005-08-05 00:44:28 -0400765 config_record = (char *)kmalloc(count + 1, GFP_KERNEL);
766 if (!config_record)
Luming Yu79cda7d2005-08-03 18:07:59 -0400767 return_VALUE(-ENOMEM);
Luming Yufb9802f2005-03-18 18:03:45 -0500768
769 if (copy_from_user(config_record, buffer, count)) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400770 kfree(config_record);
Len Brown64684632006-06-26 23:41:38 -0400771 printk(KERN_ERR PREFIX "Invalid data\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500772 return_VALUE(-EINVAL);
773 }
Luming Yu79cda7d2005-08-03 18:07:59 -0400774 config_record[count] = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500775
776 ret = get_parms(config_record,
777 &cmd,
Luming Yu79cda7d2005-08-03 18:07:59 -0400778 &bus_handle,
779 &bus_method,
780 &action_handle,
781 &method, &internal_event_num, &external_event_num);
782
783 kfree(config_record);
Len Brown4be44fc2005-08-05 00:44:28 -0400784 if (IS_OTHERS(internal_event_num))
Luming Yu79cda7d2005-08-03 18:07:59 -0400785 goto do_fail;
Luming Yufb9802f2005-03-18 18:03:45 -0500786 if (ret != 6) {
Len Brown4be44fc2005-08-05 00:44:28 -0400787 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400788 kfree(bus_handle);
789 kfree(bus_method);
790 kfree(action_handle);
791 kfree(method);
Len Brown64684632006-06-26 23:41:38 -0400792 printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret);
Luming Yufb9802f2005-03-18 18:03:45 -0500793 return_VALUE(-EINVAL);
794 }
795
796 key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL);
Len Brown4be44fc2005-08-05 00:44:28 -0400797 if (!key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400798 goto do_fail;
799 memset(key, 0, sizeof(union acpi_hotkey));
Len Brown4be44fc2005-08-05 00:44:28 -0400800 if (cmd == 1) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400801 union acpi_hotkey *tmp = NULL;
802 tmp = get_hotkey_by_event(&global_hotkey_list,
Len Brown4be44fc2005-08-05 00:44:28 -0400803 internal_event_num);
804 if (!tmp)
Len Brown64684632006-06-26 23:41:38 -0400805 printk(KERN_ERR PREFIX "Invalid key\n");
Luming Yu79cda7d2005-08-03 18:07:59 -0400806 else
807 memcpy(key, tmp, sizeof(union acpi_hotkey));
808 goto cont_cmd;
809 }
810 if (IS_EVENT(internal_event_num)) {
811 kfree(bus_method);
812 ret = init_hotkey_device(key, bus_handle, action_handle, method,
Len Brown4be44fc2005-08-05 00:44:28 -0400813 internal_event_num,
814 external_event_num);
Luming Yu79cda7d2005-08-03 18:07:59 -0400815 } else
816 ret = init_poll_hotkey_device(key, bus_handle, bus_method,
Len Brown4be44fc2005-08-05 00:44:28 -0400817 action_handle, method,
818 internal_event_num);
Luming Yu79cda7d2005-08-03 18:07:59 -0400819 if (ret) {
820 kfree(bus_handle);
821 kfree(action_handle);
Len Brown4be44fc2005-08-05 00:44:28 -0400822 if (IS_EVENT(internal_event_num))
Luming Yu79cda7d2005-08-03 18:07:59 -0400823 free_hotkey_buffer(key);
824 else
825 free_poll_hotkey_buffer(key);
Luming Yufb9802f2005-03-18 18:03:45 -0500826 kfree(key);
Len Brown64684632006-06-26 23:41:38 -0400827 printk(KERN_ERR PREFIX "Invalid hotkey\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500828 return_VALUE(-EINVAL);
829 }
Luming Yu79cda7d2005-08-03 18:07:59 -0400830
Len Brown4be44fc2005-08-05 00:44:28 -0400831 cont_cmd:
Luming Yu79cda7d2005-08-03 18:07:59 -0400832 kfree(bus_handle);
833 kfree(action_handle);
834
Luming Yufb9802f2005-03-18 18:03:45 -0500835 switch (cmd) {
836 case 0:
Len Brown4be44fc2005-08-05 00:44:28 -0400837 if (get_hotkey_by_event
838 (&global_hotkey_list, key->link.hotkey_standard_num))
Luming Yu79cda7d2005-08-03 18:07:59 -0400839 goto fail_out;
840 else
841 hotkey_add(key);
Luming Yufb9802f2005-03-18 18:03:45 -0500842 break;
843 case 1:
844 hotkey_remove(key);
845 break;
846 case 2:
Len Brown4be44fc2005-08-05 00:44:28 -0400847 if (hotkey_update(key))
Luming Yu79cda7d2005-08-03 18:07:59 -0400848 goto fail_out;
Luming Yufb9802f2005-03-18 18:03:45 -0500849 break;
850 default:
Luming Yu79cda7d2005-08-03 18:07:59 -0400851 goto fail_out;
Luming Yufb9802f2005-03-18 18:03:45 -0500852 break;
853 }
854 return_VALUE(count);
Len Brown4be44fc2005-08-05 00:44:28 -0400855 fail_out:
856 if (IS_EVENT(internal_event_num))
Luming Yu79cda7d2005-08-03 18:07:59 -0400857 free_hotkey_buffer(key);
858 else
859 free_poll_hotkey_buffer(key);
860 kfree(key);
Len Brown64684632006-06-26 23:41:38 -0400861 printk(KERN_ERR PREFIX "invalid key\n");
Luming Yu79cda7d2005-08-03 18:07:59 -0400862 return_VALUE(-EINVAL);
Luming Yufb9802f2005-03-18 18:03:45 -0500863}
864
Luming Yu79cda7d2005-08-03 18:07:59 -0400865/*
Luming Yufb9802f2005-03-18 18:03:45 -0500866 * This function evaluates an ACPI method, given an int as parameter, the
867 * method is searched within the scope of the handle, can be NULL. The output
868 * of the method is written is output, which can also be NULL
869 *
870 * returns 1 if write is successful, 0 else.
871 */
872static int write_acpi_int(acpi_handle handle, const char *method, int val,
873 struct acpi_buffer *output)
874{
875 struct acpi_object_list params; /* list of input parameters (an int here) */
876 union acpi_object in_obj; /* the only param we use */
877 acpi_status status;
878
879 ACPI_FUNCTION_TRACE("write_acpi_int");
880 params.count = 1;
881 params.pointer = &in_obj;
882 in_obj.type = ACPI_TYPE_INTEGER;
883 in_obj.integer.value = val;
884
885 status = acpi_evaluate_object(handle, (char *)method, &params, output);
886
887 return_VALUE(status == AE_OK);
888}
889
Len Brown4be44fc2005-08-05 00:44:28 -0400890static int read_acpi_int(acpi_handle handle, const char *method,
891 union acpi_object *val)
Luming Yufb9802f2005-03-18 18:03:45 -0500892{
893 struct acpi_buffer output;
894 union acpi_object out_obj;
895 acpi_status status;
896
897 ACPI_FUNCTION_TRACE("read_acpi_int");
898 output.length = sizeof(out_obj);
899 output.pointer = &out_obj;
900
901 status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
Len Brown4be44fc2005-08-05 00:44:28 -0400902 if (val) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400903 val->integer.value = out_obj.integer.value;
904 val->type = out_obj.type;
905 } else
Len Brown64684632006-06-26 23:41:38 -0400906 printk(KERN_ERR PREFIX "null val pointer\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500907 return_VALUE((status == AE_OK)
908 && (out_obj.type == ACPI_TYPE_INTEGER));
909}
910
Luming Yu79cda7d2005-08-03 18:07:59 -0400911static union acpi_hotkey *get_hotkey_by_event(struct
Len Brown4be44fc2005-08-05 00:44:28 -0400912 acpi_hotkey_list
913 *hotkey_list, int event)
Luming Yufb9802f2005-03-18 18:03:45 -0500914{
Luming Yu79cda7d2005-08-03 18:07:59 -0400915 struct list_head *entries;
Luming Yufb9802f2005-03-18 18:03:45 -0500916
Luming Yu79cda7d2005-08-03 18:07:59 -0400917 list_for_each(entries, hotkey_list->entries) {
Luming Yufb9802f2005-03-18 18:03:45 -0500918 union acpi_hotkey *key =
919 container_of(entries, union acpi_hotkey, entries);
Luming Yu79cda7d2005-08-03 18:07:59 -0400920 if (key->link.hotkey_standard_num == event) {
Len Brown4be44fc2005-08-05 00:44:28 -0400921 return (key);
Luming Yufb9802f2005-03-18 18:03:45 -0500922 }
923 }
Len Brown4be44fc2005-08-05 00:44:28 -0400924 return (NULL);
Luming Yufb9802f2005-03-18 18:03:45 -0500925}
926
Luming Yu79cda7d2005-08-03 18:07:59 -0400927/*
Luming Yufb9802f2005-03-18 18:03:45 -0500928 * user call AML method interface:
929 * Call convention:
930 * echo "event_num: arg type : value"
931 * example: echo "1:1:30" > /proc/acpi/action
932 * Just support 1 integer arg passing to AML method
933 */
934
935static ssize_t hotkey_execute_aml_method(struct file *file,
936 const char __user * buffer,
937 size_t count, loff_t * data)
938{
939 struct acpi_hotkey_list *hotkey_list = &global_hotkey_list;
Luming Yu79cda7d2005-08-03 18:07:59 -0400940 char *arg;
Len Brown4be44fc2005-08-05 00:44:28 -0400941 int event, method_type, type, value;
Luming Yu79cda7d2005-08-03 18:07:59 -0400942 union acpi_hotkey *key;
Luming Yufb9802f2005-03-18 18:03:45 -0500943
944 ACPI_FUNCTION_TRACE("hotkey_execte_aml_method");
945
Len Brown4be44fc2005-08-05 00:44:28 -0400946 arg = (char *)kmalloc(count + 1, GFP_KERNEL);
947 if (!arg)
Luming Yu79cda7d2005-08-03 18:07:59 -0400948 return_VALUE(-ENOMEM);
Len Brown4be44fc2005-08-05 00:44:28 -0400949 arg[count] = 0;
Luming Yufb9802f2005-03-18 18:03:45 -0500950
951 if (copy_from_user(arg, buffer, count)) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400952 kfree(arg);
Len Brown64684632006-06-26 23:41:38 -0400953 printk(KERN_ERR PREFIX "Invalid argument 2\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500954 return_VALUE(-EINVAL);
955 }
956
Len Brown4be44fc2005-08-05 00:44:28 -0400957 if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) !=
958 4) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400959 kfree(arg);
Len Brown64684632006-06-26 23:41:38 -0400960 printk(KERN_ERR PREFIX "Invalid argument 3\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500961 return_VALUE(-EINVAL);
962 }
Luming Yu79cda7d2005-08-03 18:07:59 -0400963 kfree(arg);
Luming Yufb9802f2005-03-18 18:03:45 -0500964 if (type == ACPI_TYPE_INTEGER) {
Luming Yu79cda7d2005-08-03 18:07:59 -0400965 key = get_hotkey_by_event(hotkey_list, event);
Len Brown4be44fc2005-08-05 00:44:28 -0400966 if (!key)
Luming Yu79cda7d2005-08-03 18:07:59 -0400967 goto do_fail;
Luming Yufb9802f2005-03-18 18:03:45 -0500968 if (IS_EVENT(event))
Luming Yu79cda7d2005-08-03 18:07:59 -0400969 write_acpi_int(key->event_hotkey.action_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400970 key->event_hotkey.action_method, value,
971 NULL);
Luming Yufb9802f2005-03-18 18:03:45 -0500972 else if (IS_POLL(event)) {
Len Brown4be44fc2005-08-05 00:44:28 -0400973 if (method_type == POLL_METHOD)
Luming Yu79cda7d2005-08-03 18:07:59 -0400974 read_acpi_int(key->poll_hotkey.poll_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400975 key->poll_hotkey.poll_method,
976 key->poll_hotkey.poll_result);
977 else if (method_type == ACTION_METHOD)
Luming Yu79cda7d2005-08-03 18:07:59 -0400978 write_acpi_int(key->poll_hotkey.action_handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400979 key->poll_hotkey.action_method,
980 value, NULL);
Luming Yu79cda7d2005-08-03 18:07:59 -0400981 else
982 goto do_fail;
983
Luming Yufb9802f2005-03-18 18:03:45 -0500984 }
985 } else {
Len Browncece9292006-06-26 23:04:31 -0400986 printk(KERN_WARNING "Not supported\n");
Luming Yufb9802f2005-03-18 18:03:45 -0500987 return_VALUE(-EINVAL);
988 }
Luming Yufb9802f2005-03-18 18:03:45 -0500989 return_VALUE(count);
Len Brown4be44fc2005-08-05 00:44:28 -0400990 do_fail:
Luming Yu79cda7d2005-08-03 18:07:59 -0400991 return_VALUE(-EINVAL);
992
Luming Yufb9802f2005-03-18 18:03:45 -0500993}
994
995static int __init hotkey_init(void)
996{
997 int result;
998 mode_t mode = S_IFREG | S_IRUGO | S_IWUGO;
999
1000 ACPI_FUNCTION_TRACE("hotkey_init");
1001
1002 if (acpi_disabled)
1003 return -ENODEV;
1004
1005 if (acpi_specific_hotkey_enabled) {
1006 printk("Using specific hotkey driver\n");
1007 return -ENODEV;
1008 }
1009
1010 hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir);
1011 if (!hotkey_proc_dir) {
Luming Yufb9802f2005-03-18 18:03:45 -05001012 return (-ENODEV);
1013 }
1014 hotkey_proc_dir->owner = THIS_MODULE;
1015
1016 hotkey_config =
1017 create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir);
1018 if (!hotkey_config) {
Luming Yu79cda7d2005-08-03 18:07:59 -04001019 goto do_fail1;
Luming Yufb9802f2005-03-18 18:03:45 -05001020 } else {
1021 hotkey_config->proc_fops = &hotkey_config_fops;
1022 hotkey_config->data = &global_hotkey_list;
1023 hotkey_config->owner = THIS_MODULE;
1024 hotkey_config->uid = 0;
1025 hotkey_config->gid = 0;
1026 }
1027
1028 hotkey_poll_config =
1029 create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir);
1030 if (!hotkey_poll_config) {
Luming Yu79cda7d2005-08-03 18:07:59 -04001031 goto do_fail2;
Luming Yufb9802f2005-03-18 18:03:45 -05001032 } else {
1033 hotkey_poll_config->proc_fops = &hotkey_poll_config_fops;
1034 hotkey_poll_config->data = &global_hotkey_list;
1035 hotkey_poll_config->owner = THIS_MODULE;
1036 hotkey_poll_config->uid = 0;
1037 hotkey_poll_config->gid = 0;
1038 }
1039
1040 hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir);
1041 if (!hotkey_action) {
Luming Yu79cda7d2005-08-03 18:07:59 -04001042 goto do_fail3;
Luming Yufb9802f2005-03-18 18:03:45 -05001043 } else {
1044 hotkey_action->proc_fops = &hotkey_action_fops;
1045 hotkey_action->owner = THIS_MODULE;
1046 hotkey_action->uid = 0;
1047 hotkey_action->gid = 0;
1048 }
1049
1050 hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir);
1051 if (!hotkey_info) {
Luming Yu79cda7d2005-08-03 18:07:59 -04001052 goto do_fail4;
Luming Yufb9802f2005-03-18 18:03:45 -05001053 } else {
1054 hotkey_info->proc_fops = &hotkey_info_fops;
1055 hotkey_info->owner = THIS_MODULE;
1056 hotkey_info->uid = 0;
1057 hotkey_info->gid = 0;
1058 }
1059
1060 result = acpi_bus_register_driver(&hotkey_driver);
Luming Yu79cda7d2005-08-03 18:07:59 -04001061 if (result < 0)
1062 goto do_fail5;
Luming Yufb9802f2005-03-18 18:03:45 -05001063 global_hotkey_list.count = 0;
1064 global_hotkey_list.entries = &hotkey_entries;
1065
1066 INIT_LIST_HEAD(&hotkey_entries);
1067
1068 return (0);
Luming Yu79cda7d2005-08-03 18:07:59 -04001069
Len Brown4be44fc2005-08-05 00:44:28 -04001070 do_fail5:
Luming Yu79cda7d2005-08-03 18:07:59 -04001071 remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001072 do_fail4:
Luming Yu79cda7d2005-08-03 18:07:59 -04001073 remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001074 do_fail3:
Luming Yu79cda7d2005-08-03 18:07:59 -04001075 remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001076 do_fail2:
Luming Yu79cda7d2005-08-03 18:07:59 -04001077 remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir);
Len Brown4be44fc2005-08-05 00:44:28 -04001078 do_fail1:
Luming Yu79cda7d2005-08-03 18:07:59 -04001079 remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
1080 return (-ENODEV);
Luming Yufb9802f2005-03-18 18:03:45 -05001081}
1082
1083static void __exit hotkey_exit(void)
1084{
1085 struct list_head *entries, *next;
1086
Luming Yu79cda7d2005-08-03 18:07:59 -04001087 ACPI_FUNCTION_TRACE("hotkey_exit");
Luming Yufb9802f2005-03-18 18:03:45 -05001088
1089 list_for_each_safe(entries, next, global_hotkey_list.entries) {
1090 union acpi_hotkey *key =
1091 container_of(entries, union acpi_hotkey, entries);
1092
1093 acpi_os_wait_events_complete(NULL);
1094 list_del(&key->link.entries);
1095 global_hotkey_list.count--;
1096 free_hotkey_device(key);
1097 }
1098 acpi_bus_unregister_driver(&hotkey_driver);
1099 remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir);
1100 remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir);
1101 remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir);
1102 remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir);
1103 remove_proc_entry(HOTKEY_PROC, acpi_root_dir);
1104 return;
1105}
1106
1107module_init(hotkey_init);
1108module_exit(hotkey_exit);