Commit | Line | Data |
---|---|---|
79cda7d0 LY |
1 | /* |
2 | * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $) | |
fb9802fa LY |
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 | ||
79cda7d0 | 54 | #define RESULT_STR_LEN 80 |
fb9802fa | 55 | |
79cda7d0 LY |
56 | #define ACTION_METHOD 0 |
57 | #define POLL_METHOD 1 | |
fb9802fa | 58 | |
79cda7d0 LY |
59 | #define IS_EVENT(e) ((e) <= 10000 && (e) >0) |
60 | #define IS_POLL(e) ((e) > 10000) | |
61 | #define IS_OTHERS(e) ((e)<=0 || (e)>=20000) | |
fb9802fa LY |
62 | #define _COMPONENT ACPI_HOTKEY_COMPONENT |
63 | ACPI_MODULE_NAME("acpi_hotkey") | |
64 | ||
4be44fcd | 65 | MODULE_AUTHOR("luming.yu@intel.com"); |
fb9802fa LY |
66 | MODULE_DESCRIPTION(ACPI_HOTK_NAME); |
67 | MODULE_LICENSE("GPL"); | |
68 | ||
69 | /* standardized internal hotkey number/event */ | |
70 | enum { | |
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 */ | |
95 | static struct proc_dir_entry *hotkey_proc_dir; | |
96 | static struct proc_dir_entry *hotkey_config; | |
97 | static struct proc_dir_entry *hotkey_poll_config; | |
98 | static struct proc_dir_entry *hotkey_action; | |
99 | static struct proc_dir_entry *hotkey_info; | |
100 | ||
101 | /* linkage for all type of hotkey */ | |
102 | struct 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 */ | |
109 | struct 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 | ||
79cda7d0 | 118 | /* |
fb9802fa LY |
119 | * 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 */ | |
128 | struct 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 */ | |
79cda7d0 | 135 | union acpi_object *poll_result; /* polling_result */ |
fb9802fa LY |
136 | struct proc_dir_entry *proc; |
137 | }; | |
138 | ||
139 | /* hotkey object union */ | |
140 | union 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 */ | |
148 | struct acpi_hotkey_list { | |
149 | struct list_head *entries; | |
150 | int count; | |
151 | }; | |
152 | ||
153 | static int auto_hotkey_add(struct acpi_device *device); | |
154 | static int auto_hotkey_remove(struct acpi_device *device, int type); | |
155 | ||
156 | static 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 | ||
79cda7d0 LY |
166 | static void free_hotkey_device(union acpi_hotkey *key); |
167 | static void free_hotkey_buffer(union acpi_hotkey *key); | |
168 | static void free_poll_hotkey_buffer(union acpi_hotkey *key); | |
fb9802fa | 169 | static int hotkey_open_config(struct inode *inode, struct file *file); |
79cda7d0 | 170 | static int hotkey_poll_open_config(struct inode *inode, struct file *file); |
fb9802fa LY |
171 | static ssize_t hotkey_write_config(struct file *file, |
172 | const char __user * buffer, | |
173 | size_t count, loff_t * data); | |
fb9802fa LY |
174 | static int hotkey_info_open_fs(struct inode *inode, struct file *file); |
175 | static int hotkey_action_open_fs(struct inode *inode, struct file *file); | |
176 | static ssize_t hotkey_execute_aml_method(struct file *file, | |
177 | const char __user * buffer, | |
178 | size_t count, loff_t * data); | |
179 | static int hotkey_config_seq_show(struct seq_file *seq, void *offset); | |
79cda7d0 | 180 | static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset); |
fb9802fa | 181 | static int hotkey_polling_open_fs(struct inode *inode, struct file *file); |
79cda7d0 | 182 | static union acpi_hotkey *get_hotkey_by_event(struct |
4be44fcd LB |
183 | acpi_hotkey_list |
184 | *hotkey_list, int event); | |
fb9802fa LY |
185 | |
186 | /* event based config */ | |
187 | static 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 */ | |
196 | static struct file_operations hotkey_poll_config_fops = { | |
79cda7d0 | 197 | .open = hotkey_poll_open_config, |
fb9802fa | 198 | .read = seq_read, |
79cda7d0 | 199 | .write = hotkey_write_config, |
fb9802fa LY |
200 | .llseek = seq_lseek, |
201 | .release = single_release, | |
202 | }; | |
203 | ||
204 | /* hotkey driver info */ | |
205 | static 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 */ | |
213 | static 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 */ | |
222 | static 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 | ||
229 | struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */ | |
230 | struct list_head hotkey_entries; /* head of the list of hotkey_list */ | |
231 | ||
232 | static int hotkey_info_seq_show(struct seq_file *seq, void *offset) | |
233 | { | |
234 | ACPI_FUNCTION_TRACE("hotkey_info_seq_show"); | |
235 | ||
79cda7d0 | 236 | seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION); |
fb9802fa LY |
237 | |
238 | return_VALUE(0); | |
239 | } | |
240 | ||
241 | static 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 | ||
246 | static char *format_result(union acpi_object *object) | |
247 | { | |
79cda7d0 | 248 | char *buf = NULL; |
4be44fcd | 249 | |
79cda7d0 LY |
250 | buf = (char *)kmalloc(RESULT_STR_LEN, GFP_KERNEL); |
251 | if (buf) | |
252 | memset(buf, 0, RESULT_STR_LEN); | |
253 | else | |
254 | goto do_fail; | |
fb9802fa LY |
255 | |
256 | /* Now, just support integer type */ | |
257 | if (object->type == ACPI_TYPE_INTEGER) | |
79cda7d0 | 258 | sprintf(buf, "%d\n", (u32) object->integer.value); |
4be44fcd | 259 | do_fail: |
79cda7d0 | 260 | return (buf); |
fb9802fa LY |
261 | } |
262 | ||
263 | static 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; | |
79cda7d0 | 267 | char *buf; |
fb9802fa LY |
268 | |
269 | ACPI_FUNCTION_TRACE("hotkey_polling_seq_show"); | |
270 | ||
4be44fcd | 271 | if (poll_hotkey->poll_result) { |
79cda7d0 | 272 | buf = format_result(poll_hotkey->poll_result); |
4be44fcd | 273 | if (buf) |
79cda7d0 LY |
274 | seq_printf(seq, "%s", buf); |
275 | kfree(buf); | |
276 | } | |
fb9802fa LY |
277 | return_VALUE(0); |
278 | } | |
279 | ||
280 | static 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 | ||
285 | static 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 */ | |
291 | static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list) | |
292 | { | |
79cda7d0 LY |
293 | struct list_head *entries; |
294 | int val = -1; | |
fb9802fa LY |
295 | |
296 | ACPI_FUNCTION_TRACE("hotkey_get_internal_event"); | |
297 | ||
79cda7d0 | 298 | list_for_each(entries, list->entries) { |
fb9802fa LY |
299 | union acpi_hotkey *key = |
300 | container_of(entries, union acpi_hotkey, entries); | |
301 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT | |
4be44fcd | 302 | && key->event_hotkey.external_hotkey_num == event) { |
fb9802fa | 303 | val = key->link.hotkey_standard_num; |
79cda7d0 LY |
304 | break; |
305 | } | |
fb9802fa LY |
306 | } |
307 | ||
308 | return_VALUE(val); | |
309 | } | |
310 | ||
311 | static void | |
312 | acpi_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); | |
79cda7d0 | 323 | acpi_bus_generate_event(device, internal_event, 0); |
fb9802fa LY |
324 | |
325 | return_VOID; | |
326 | } | |
327 | ||
328 | /* Need to invent automatically hotkey add method */ | |
329 | static 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 */ | |
336 | static 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 */ | |
343 | static int create_polling_proc(union acpi_hotkey *device) | |
344 | { | |
345 | struct proc_dir_entry *proc; | |
4be44fcd | 346 | char proc_name[80]; |
8de7a63b | 347 | mode_t mode; |
fb9802fa LY |
348 | |
349 | ACPI_FUNCTION_TRACE("create_polling_proc"); | |
8de7a63b | 350 | mode = S_IFREG | S_IRUGO | S_IWUGO; |
fb9802fa | 351 | |
79cda7d0 LY |
352 | sprintf(proc_name, "%d", device->link.hotkey_standard_num); |
353 | /* | |
4be44fcd LB |
354 | strcat(proc_name, device->poll_hotkey.poll_method); |
355 | */ | |
79cda7d0 | 356 | proc = create_proc_entry(proc_name, mode, hotkey_proc_dir); |
fb9802fa LY |
357 | |
358 | if (!proc) { | |
359 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
360 | "Hotkey: Unable to create %s entry\n", | |
361 | device->poll_hotkey.poll_method)); | |
362 | return_VALUE(-ENODEV); | |
363 | } else { | |
364 | proc->proc_fops = &hotkey_polling_fops; | |
365 | proc->owner = THIS_MODULE; | |
366 | proc->data = device; | |
367 | proc->uid = 0; | |
368 | proc->gid = 0; | |
369 | device->poll_hotkey.proc = proc; | |
370 | } | |
371 | return_VALUE(0); | |
372 | } | |
373 | ||
fb9802fa LY |
374 | static int hotkey_add(union acpi_hotkey *device) |
375 | { | |
376 | int status = 0; | |
377 | struct acpi_device *dev = NULL; | |
378 | ||
379 | ACPI_FUNCTION_TRACE("hotkey_add"); | |
380 | ||
381 | if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
79cda7d0 | 382 | acpi_bus_get_device(device->event_hotkey.bus_handle, &dev); |
fb9802fa | 383 | status = acpi_install_notify_handler(dev->handle, |
79cda7d0 | 384 | ACPI_DEVICE_NOTIFY, |
fb9802fa | 385 | acpi_hotkey_notify_handler, |
79cda7d0 | 386 | dev); |
fb9802fa LY |
387 | } else /* Add polling hotkey */ |
388 | create_polling_proc(device); | |
389 | ||
390 | global_hotkey_list.count++; | |
391 | ||
392 | list_add_tail(&device->link.entries, global_hotkey_list.entries); | |
393 | ||
394 | return_VALUE(status); | |
395 | } | |
396 | ||
397 | static int hotkey_remove(union acpi_hotkey *device) | |
398 | { | |
399 | struct list_head *entries, *next; | |
400 | ||
401 | ACPI_FUNCTION_TRACE("hotkey_remove"); | |
402 | ||
403 | list_for_each_safe(entries, next, global_hotkey_list.entries) { | |
404 | union acpi_hotkey *key = | |
405 | container_of(entries, union acpi_hotkey, entries); | |
406 | if (key->link.hotkey_standard_num == | |
407 | device->link.hotkey_standard_num) { | |
408 | list_del(&key->link.entries); | |
79cda7d0 | 409 | free_hotkey_device(key); |
fb9802fa LY |
410 | global_hotkey_list.count--; |
411 | break; | |
412 | } | |
413 | } | |
79cda7d0 | 414 | kfree(device); |
fb9802fa LY |
415 | return_VALUE(0); |
416 | } | |
417 | ||
4be44fcd | 418 | static int hotkey_update(union acpi_hotkey *key) |
fb9802fa | 419 | { |
79cda7d0 | 420 | struct list_head *entries; |
fb9802fa LY |
421 | |
422 | ACPI_FUNCTION_TRACE("hotkey_update"); | |
423 | ||
79cda7d0 | 424 | list_for_each(entries, global_hotkey_list.entries) { |
4be44fcd | 425 | union acpi_hotkey *tmp = |
fb9802fa | 426 | container_of(entries, union acpi_hotkey, entries); |
79cda7d0 | 427 | if (tmp->link.hotkey_standard_num == |
fb9802fa | 428 | key->link.hotkey_standard_num) { |
79cda7d0 LY |
429 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { |
430 | free_hotkey_buffer(tmp); | |
431 | tmp->event_hotkey.bus_handle = | |
4be44fcd | 432 | key->event_hotkey.bus_handle; |
79cda7d0 | 433 | tmp->event_hotkey.external_hotkey_num = |
4be44fcd | 434 | key->event_hotkey.external_hotkey_num; |
79cda7d0 | 435 | tmp->event_hotkey.action_handle = |
4be44fcd | 436 | key->event_hotkey.action_handle; |
79cda7d0 | 437 | tmp->event_hotkey.action_method = |
4be44fcd | 438 | key->event_hotkey.action_method; |
79cda7d0 LY |
439 | kfree(key); |
440 | } else { | |
441 | /* | |
4be44fcd | 442 | char proc_name[80]; |
79cda7d0 | 443 | |
4be44fcd LB |
444 | sprintf(proc_name, "%d", tmp->link.hotkey_standard_num); |
445 | strcat(proc_name, tmp->poll_hotkey.poll_method); | |
446 | remove_proc_entry(proc_name,hotkey_proc_dir); | |
447 | */ | |
79cda7d0 LY |
448 | free_poll_hotkey_buffer(tmp); |
449 | tmp->poll_hotkey.poll_handle = | |
4be44fcd | 450 | key->poll_hotkey.poll_handle; |
79cda7d0 | 451 | tmp->poll_hotkey.poll_method = |
4be44fcd | 452 | key->poll_hotkey.poll_method; |
79cda7d0 | 453 | tmp->poll_hotkey.action_handle = |
4be44fcd | 454 | key->poll_hotkey.action_handle; |
79cda7d0 | 455 | tmp->poll_hotkey.action_method = |
4be44fcd | 456 | key->poll_hotkey.action_method; |
79cda7d0 | 457 | tmp->poll_hotkey.poll_result = |
4be44fcd | 458 | key->poll_hotkey.poll_result; |
79cda7d0 | 459 | /* |
4be44fcd LB |
460 | create_polling_proc(tmp); |
461 | */ | |
79cda7d0 LY |
462 | kfree(key); |
463 | } | |
464 | return_VALUE(0); | |
fb9802fa LY |
465 | break; |
466 | } | |
467 | } | |
468 | ||
79cda7d0 | 469 | return_VALUE(-ENODEV); |
fb9802fa LY |
470 | } |
471 | ||
472 | static void free_hotkey_device(union acpi_hotkey *key) | |
473 | { | |
474 | struct acpi_device *dev; | |
fb9802fa LY |
475 | |
476 | ACPI_FUNCTION_TRACE("free_hotkey_device"); | |
477 | ||
478 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
79cda7d0 | 479 | acpi_bus_get_device(key->event_hotkey.bus_handle, &dev); |
fb9802fa LY |
480 | if (dev->handle) |
481 | acpi_remove_notify_handler(dev->handle, | |
79cda7d0 | 482 | ACPI_DEVICE_NOTIFY, |
fb9802fa | 483 | acpi_hotkey_notify_handler); |
79cda7d0 LY |
484 | free_hotkey_buffer(key); |
485 | } else { | |
4be44fcd | 486 | char proc_name[80]; |
79cda7d0 LY |
487 | |
488 | sprintf(proc_name, "%d", key->link.hotkey_standard_num); | |
489 | /* | |
4be44fcd LB |
490 | strcat(proc_name, key->poll_hotkey.poll_method); |
491 | */ | |
492 | remove_proc_entry(proc_name, hotkey_proc_dir); | |
79cda7d0 LY |
493 | free_poll_hotkey_buffer(key); |
494 | } | |
fb9802fa LY |
495 | kfree(key); |
496 | return_VOID; | |
497 | } | |
498 | ||
4be44fcd | 499 | static void free_hotkey_buffer(union acpi_hotkey *key) |
79cda7d0 LY |
500 | { |
501 | kfree(key->event_hotkey.action_method); | |
502 | } | |
503 | ||
4be44fcd | 504 | static void free_poll_hotkey_buffer(union acpi_hotkey *key) |
79cda7d0 LY |
505 | { |
506 | kfree(key->poll_hotkey.action_method); | |
507 | kfree(key->poll_hotkey.poll_method); | |
508 | kfree(key->poll_hotkey.poll_result); | |
509 | } | |
fb9802fa LY |
510 | static int |
511 | init_hotkey_device(union acpi_hotkey *key, char *bus_str, char *action_str, | |
512 | char *method, int std_num, int external_num) | |
513 | { | |
4be44fcd | 514 | acpi_handle tmp_handle; |
79cda7d0 LY |
515 | acpi_status status = AE_OK; |
516 | ||
fb9802fa LY |
517 | ACPI_FUNCTION_TRACE("init_hotkey_device"); |
518 | ||
4be44fcd | 519 | if (std_num < 0 || IS_POLL(std_num) || !key) |
79cda7d0 LY |
520 | goto do_fail; |
521 | ||
4be44fcd | 522 | if (!bus_str || !action_str || !method) |
79cda7d0 LY |
523 | goto do_fail; |
524 | ||
fb9802fa LY |
525 | key->link.hotkey_type = ACPI_HOTKEY_EVENT; |
526 | key->link.hotkey_standard_num = std_num; | |
527 | key->event_hotkey.flag = 0; | |
79cda7d0 | 528 | key->event_hotkey.action_method = method; |
fb9802fa | 529 | |
4be44fcd LB |
530 | status = |
531 | acpi_get_handle(NULL, bus_str, &(key->event_hotkey.bus_handle)); | |
532 | if (ACPI_FAILURE(status)) | |
79cda7d0 LY |
533 | goto do_fail; |
534 | key->event_hotkey.external_hotkey_num = external_num; | |
4be44fcd LB |
535 | status = |
536 | acpi_get_handle(NULL, action_str, | |
537 | &(key->event_hotkey.action_handle)); | |
538 | if (ACPI_FAILURE(status)) | |
79cda7d0 LY |
539 | goto do_fail; |
540 | status = acpi_get_handle(key->event_hotkey.action_handle, | |
4be44fcd | 541 | method, &tmp_handle); |
79cda7d0 LY |
542 | if (ACPI_FAILURE(status)) |
543 | goto do_fail; | |
544 | return_VALUE(AE_OK); | |
4be44fcd | 545 | do_fail: |
79cda7d0 | 546 | return_VALUE(-ENODEV); |
fb9802fa LY |
547 | } |
548 | ||
549 | static int | |
550 | init_poll_hotkey_device(union acpi_hotkey *key, | |
551 | char *poll_str, | |
552 | char *poll_method, | |
553 | char *action_str, char *action_method, int std_num) | |
554 | { | |
79cda7d0 | 555 | acpi_status status = AE_OK; |
4be44fcd | 556 | acpi_handle tmp_handle; |
79cda7d0 | 557 | |
fb9802fa LY |
558 | ACPI_FUNCTION_TRACE("init_poll_hotkey_device"); |
559 | ||
4be44fcd | 560 | if (std_num < 0 || IS_EVENT(std_num) || !key) |
79cda7d0 LY |
561 | goto do_fail; |
562 | ||
4be44fcd | 563 | if (!poll_str || !poll_method || !action_str || !action_method) |
79cda7d0 LY |
564 | goto do_fail; |
565 | ||
fb9802fa LY |
566 | key->link.hotkey_type = ACPI_HOTKEY_POLLING; |
567 | key->link.hotkey_standard_num = std_num; | |
568 | key->poll_hotkey.flag = 0; | |
fb9802fa | 569 | key->poll_hotkey.poll_method = poll_method; |
79cda7d0 LY |
570 | key->poll_hotkey.action_method = action_method; |
571 | ||
4be44fcd LB |
572 | status = |
573 | acpi_get_handle(NULL, poll_str, &(key->poll_hotkey.poll_handle)); | |
574 | if (ACPI_FAILURE(status)) | |
79cda7d0 LY |
575 | goto do_fail; |
576 | status = acpi_get_handle(key->poll_hotkey.poll_handle, | |
4be44fcd LB |
577 | poll_method, &tmp_handle); |
578 | if (ACPI_FAILURE(status)) | |
579 | goto do_fail; | |
580 | status = | |
581 | acpi_get_handle(NULL, action_str, | |
582 | &(key->poll_hotkey.action_handle)); | |
79cda7d0 LY |
583 | if (ACPI_FAILURE(status)) |
584 | goto do_fail; | |
585 | status = acpi_get_handle(key->poll_hotkey.action_handle, | |
4be44fcd | 586 | action_method, &tmp_handle); |
79cda7d0 LY |
587 | if (ACPI_FAILURE(status)) |
588 | goto do_fail; | |
fb9802fa LY |
589 | key->poll_hotkey.poll_result = |
590 | (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL); | |
4be44fcd | 591 | if (!key->poll_hotkey.poll_result) |
79cda7d0 LY |
592 | goto do_fail; |
593 | return_VALUE(AE_OK); | |
4be44fcd | 594 | do_fail: |
79cda7d0 | 595 | return_VALUE(-ENODEV); |
fb9802fa LY |
596 | } |
597 | ||
fb9802fa LY |
598 | static int hotkey_open_config(struct inode *inode, struct file *file) |
599 | { | |
600 | ACPI_FUNCTION_TRACE("hotkey_open_config"); | |
601 | return_VALUE(single_open | |
602 | (file, hotkey_config_seq_show, PDE(inode)->data)); | |
603 | } | |
604 | ||
79cda7d0 LY |
605 | static int hotkey_poll_open_config(struct inode *inode, struct file *file) |
606 | { | |
607 | ACPI_FUNCTION_TRACE("hotkey_poll_open_config"); | |
608 | return_VALUE(single_open | |
609 | (file, hotkey_poll_config_seq_show, PDE(inode)->data)); | |
610 | } | |
611 | ||
fb9802fa LY |
612 | static int hotkey_config_seq_show(struct seq_file *seq, void *offset) |
613 | { | |
614 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
79cda7d0 | 615 | struct list_head *entries; |
fb9802fa LY |
616 | char bus_name[ACPI_PATHNAME_MAX] = { 0 }; |
617 | char action_name[ACPI_PATHNAME_MAX] = { 0 }; | |
618 | struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; | |
619 | struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; | |
620 | ||
621 | ACPI_FUNCTION_TRACE(("hotkey_config_seq_show")); | |
622 | ||
79cda7d0 | 623 | list_for_each(entries, hotkey_list->entries) { |
fb9802fa LY |
624 | union acpi_hotkey *key = |
625 | container_of(entries, union acpi_hotkey, entries); | |
626 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
627 | acpi_get_name(key->event_hotkey.bus_handle, | |
628 | ACPI_NAME_TYPE_MAX, &bus); | |
629 | acpi_get_name(key->event_hotkey.action_handle, | |
630 | ACPI_NAME_TYPE_MAX, &act); | |
79cda7d0 | 631 | seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name, |
fb9802fa LY |
632 | action_name, |
633 | key->event_hotkey.action_method, | |
634 | key->link.hotkey_standard_num, | |
635 | key->event_hotkey.external_hotkey_num); | |
79cda7d0 LY |
636 | } |
637 | } | |
638 | seq_puts(seq, "\n"); | |
639 | return_VALUE(0); | |
640 | } | |
641 | ||
642 | static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset) | |
643 | { | |
644 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
645 | struct list_head *entries; | |
646 | char bus_name[ACPI_PATHNAME_MAX] = { 0 }; | |
647 | char action_name[ACPI_PATHNAME_MAX] = { 0 }; | |
648 | struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; | |
649 | struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; | |
650 | ||
651 | ACPI_FUNCTION_TRACE(("hotkey_config_seq_show")); | |
652 | ||
653 | list_for_each(entries, hotkey_list->entries) { | |
654 | union acpi_hotkey *key = | |
655 | container_of(entries, union acpi_hotkey, entries); | |
656 | if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) { | |
fb9802fa LY |
657 | acpi_get_name(key->poll_hotkey.poll_handle, |
658 | ACPI_NAME_TYPE_MAX, &bus); | |
659 | acpi_get_name(key->poll_hotkey.action_handle, | |
660 | ACPI_NAME_TYPE_MAX, &act); | |
79cda7d0 | 661 | seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name, |
fb9802fa LY |
662 | key->poll_hotkey.poll_method, |
663 | action_name, | |
664 | key->poll_hotkey.action_method, | |
665 | key->link.hotkey_standard_num); | |
666 | } | |
667 | } | |
668 | seq_puts(seq, "\n"); | |
fb9802fa LY |
669 | return_VALUE(0); |
670 | } | |
671 | ||
672 | static int | |
673 | get_parms(char *config_record, | |
674 | int *cmd, | |
79cda7d0 LY |
675 | char **bus_handle, |
676 | char **bus_method, | |
677 | char **action_handle, | |
678 | char **method, int *internal_event_num, int *external_event_num) | |
fb9802fa | 679 | { |
79cda7d0 | 680 | char *tmp, *tmp1, count; |
fb9802fa LY |
681 | ACPI_FUNCTION_TRACE(("get_parms")); |
682 | ||
683 | sscanf(config_record, "%d", cmd); | |
684 | ||
4be44fcd LB |
685 | if (*cmd == 1) { |
686 | if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != | |
687 | 2) | |
79cda7d0 LY |
688 | goto do_fail; |
689 | else | |
690 | return (6); | |
691 | } | |
fb9802fa | 692 | tmp = strchr(config_record, ':'); |
79cda7d0 LY |
693 | if (!tmp) |
694 | goto do_fail; | |
fb9802fa LY |
695 | tmp++; |
696 | tmp1 = strchr(tmp, ':'); | |
79cda7d0 LY |
697 | if (!tmp1) |
698 | goto do_fail; | |
699 | ||
700 | count = tmp1 - tmp; | |
4be44fcd LB |
701 | *bus_handle = (char *)kmalloc(count + 1, GFP_KERNEL); |
702 | if (!*bus_handle) | |
79cda7d0 LY |
703 | goto do_fail; |
704 | strncpy(*bus_handle, tmp, count); | |
705 | *(*bus_handle + count) = 0; | |
fb9802fa LY |
706 | |
707 | tmp = tmp1; | |
708 | tmp++; | |
709 | tmp1 = strchr(tmp, ':'); | |
79cda7d0 LY |
710 | if (!tmp1) |
711 | goto do_fail; | |
712 | count = tmp1 - tmp; | |
4be44fcd LB |
713 | *bus_method = (char *)kmalloc(count + 1, GFP_KERNEL); |
714 | if (!*bus_method) | |
79cda7d0 LY |
715 | goto do_fail; |
716 | strncpy(*bus_method, tmp, count); | |
717 | *(*bus_method + count) = 0; | |
fb9802fa LY |
718 | |
719 | tmp = tmp1; | |
720 | tmp++; | |
721 | tmp1 = strchr(tmp, ':'); | |
79cda7d0 LY |
722 | if (!tmp1) |
723 | goto do_fail; | |
724 | count = tmp1 - tmp; | |
4be44fcd | 725 | *action_handle = (char *)kmalloc(count + 1, GFP_KERNEL); |
79cda7d0 LY |
726 | strncpy(*action_handle, tmp, count); |
727 | *(*action_handle + count) = 0; | |
fb9802fa LY |
728 | |
729 | tmp = tmp1; | |
730 | tmp++; | |
731 | tmp1 = strchr(tmp, ':'); | |
79cda7d0 LY |
732 | if (!tmp1) |
733 | goto do_fail; | |
734 | count = tmp1 - tmp; | |
4be44fcd LB |
735 | *method = (char *)kmalloc(count + 1, GFP_KERNEL); |
736 | if (!*method) | |
79cda7d0 LY |
737 | goto do_fail; |
738 | strncpy(*method, tmp, count); | |
739 | *(*method + count) = 0; | |
740 | ||
4be44fcd LB |
741 | if (sscanf(tmp1 + 1, "%d:%d", internal_event_num, external_event_num) <= |
742 | 0) | |
79cda7d0 | 743 | goto do_fail; |
fb9802fa | 744 | |
fb9802fa | 745 | return_VALUE(6); |
4be44fcd | 746 | do_fail: |
79cda7d0 | 747 | return_VALUE(-1); |
fb9802fa LY |
748 | } |
749 | ||
750 | /* count is length for one input record */ | |
751 | static ssize_t hotkey_write_config(struct file *file, | |
752 | const char __user * buffer, | |
753 | size_t count, loff_t * data) | |
754 | { | |
79cda7d0 LY |
755 | char *config_record = NULL; |
756 | char *bus_handle = NULL; | |
757 | char *bus_method = NULL; | |
758 | char *action_handle = NULL; | |
759 | char *method = NULL; | |
fb9802fa LY |
760 | int cmd, internal_event_num, external_event_num; |
761 | int ret = 0; | |
762 | union acpi_hotkey *key = NULL; | |
763 | ||
764 | ACPI_FUNCTION_TRACE(("hotkey_write_config")); | |
765 | ||
4be44fcd LB |
766 | config_record = (char *)kmalloc(count + 1, GFP_KERNEL); |
767 | if (!config_record) | |
79cda7d0 | 768 | return_VALUE(-ENOMEM); |
fb9802fa LY |
769 | |
770 | if (copy_from_user(config_record, buffer, count)) { | |
79cda7d0 | 771 | kfree(config_record); |
fb9802fa LY |
772 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data \n")); |
773 | return_VALUE(-EINVAL); | |
774 | } | |
79cda7d0 | 775 | config_record[count] = 0; |
fb9802fa LY |
776 | |
777 | ret = get_parms(config_record, | |
778 | &cmd, | |
79cda7d0 LY |
779 | &bus_handle, |
780 | &bus_method, | |
781 | &action_handle, | |
782 | &method, &internal_event_num, &external_event_num); | |
783 | ||
784 | kfree(config_record); | |
4be44fcd | 785 | if (IS_OTHERS(internal_event_num)) |
79cda7d0 | 786 | goto do_fail; |
fb9802fa | 787 | if (ret != 6) { |
4be44fcd | 788 | do_fail: |
79cda7d0 LY |
789 | kfree(bus_handle); |
790 | kfree(bus_method); | |
791 | kfree(action_handle); | |
792 | kfree(method); | |
fb9802fa LY |
793 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
794 | "Invalid data format ret=%d\n", ret)); | |
795 | return_VALUE(-EINVAL); | |
796 | } | |
797 | ||
798 | key = kmalloc(sizeof(union acpi_hotkey), GFP_KERNEL); | |
4be44fcd | 799 | if (!key) |
79cda7d0 LY |
800 | goto do_fail; |
801 | memset(key, 0, sizeof(union acpi_hotkey)); | |
4be44fcd | 802 | if (cmd == 1) { |
79cda7d0 LY |
803 | union acpi_hotkey *tmp = NULL; |
804 | tmp = get_hotkey_by_event(&global_hotkey_list, | |
4be44fcd LB |
805 | internal_event_num); |
806 | if (!tmp) | |
79cda7d0 LY |
807 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid key")); |
808 | else | |
809 | memcpy(key, tmp, sizeof(union acpi_hotkey)); | |
810 | goto cont_cmd; | |
811 | } | |
812 | if (IS_EVENT(internal_event_num)) { | |
813 | kfree(bus_method); | |
814 | ret = init_hotkey_device(key, bus_handle, action_handle, method, | |
4be44fcd LB |
815 | internal_event_num, |
816 | external_event_num); | |
79cda7d0 LY |
817 | } else |
818 | ret = init_poll_hotkey_device(key, bus_handle, bus_method, | |
4be44fcd LB |
819 | action_handle, method, |
820 | internal_event_num); | |
79cda7d0 LY |
821 | if (ret) { |
822 | kfree(bus_handle); | |
823 | kfree(action_handle); | |
4be44fcd | 824 | if (IS_EVENT(internal_event_num)) |
79cda7d0 LY |
825 | free_hotkey_buffer(key); |
826 | else | |
827 | free_poll_hotkey_buffer(key); | |
fb9802fa LY |
828 | kfree(key); |
829 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid hotkey \n")); | |
830 | return_VALUE(-EINVAL); | |
831 | } | |
fb9802fa | 832 | |
4be44fcd | 833 | cont_cmd: |
79cda7d0 LY |
834 | kfree(bus_handle); |
835 | kfree(action_handle); | |
fb9802fa | 836 | |
fb9802fa LY |
837 | switch (cmd) { |
838 | case 0: | |
4be44fcd LB |
839 | if (get_hotkey_by_event |
840 | (&global_hotkey_list, key->link.hotkey_standard_num)) | |
79cda7d0 LY |
841 | goto fail_out; |
842 | else | |
843 | hotkey_add(key); | |
fb9802fa LY |
844 | break; |
845 | case 1: | |
846 | hotkey_remove(key); | |
847 | break; | |
848 | case 2: | |
4be44fcd | 849 | if (hotkey_update(key)) |
79cda7d0 | 850 | goto fail_out; |
fb9802fa LY |
851 | break; |
852 | default: | |
79cda7d0 | 853 | goto fail_out; |
fb9802fa LY |
854 | break; |
855 | } | |
856 | return_VALUE(count); | |
4be44fcd LB |
857 | fail_out: |
858 | if (IS_EVENT(internal_event_num)) | |
79cda7d0 LY |
859 | free_hotkey_buffer(key); |
860 | else | |
861 | free_poll_hotkey_buffer(key); | |
862 | kfree(key); | |
863 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "invalid key\n")); | |
864 | return_VALUE(-EINVAL); | |
fb9802fa LY |
865 | } |
866 | ||
79cda7d0 | 867 | /* |
fb9802fa LY |
868 | * This function evaluates an ACPI method, given an int as parameter, the |
869 | * method is searched within the scope of the handle, can be NULL. The output | |
870 | * of the method is written is output, which can also be NULL | |
871 | * | |
872 | * returns 1 if write is successful, 0 else. | |
873 | */ | |
874 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | |
875 | struct acpi_buffer *output) | |
876 | { | |
877 | struct acpi_object_list params; /* list of input parameters (an int here) */ | |
878 | union acpi_object in_obj; /* the only param we use */ | |
879 | acpi_status status; | |
880 | ||
881 | ACPI_FUNCTION_TRACE("write_acpi_int"); | |
882 | params.count = 1; | |
883 | params.pointer = &in_obj; | |
884 | in_obj.type = ACPI_TYPE_INTEGER; | |
885 | in_obj.integer.value = val; | |
886 | ||
887 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); | |
888 | ||
889 | return_VALUE(status == AE_OK); | |
890 | } | |
891 | ||
4be44fcd LB |
892 | static int read_acpi_int(acpi_handle handle, const char *method, |
893 | union acpi_object *val) | |
fb9802fa LY |
894 | { |
895 | struct acpi_buffer output; | |
896 | union acpi_object out_obj; | |
897 | acpi_status status; | |
898 | ||
899 | ACPI_FUNCTION_TRACE("read_acpi_int"); | |
900 | output.length = sizeof(out_obj); | |
901 | output.pointer = &out_obj; | |
902 | ||
903 | status = acpi_evaluate_object(handle, (char *)method, NULL, &output); | |
4be44fcd | 904 | if (val) { |
79cda7d0 LY |
905 | val->integer.value = out_obj.integer.value; |
906 | val->type = out_obj.type; | |
907 | } else | |
908 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "null val pointer")); | |
fb9802fa LY |
909 | return_VALUE((status == AE_OK) |
910 | && (out_obj.type == ACPI_TYPE_INTEGER)); | |
911 | } | |
912 | ||
79cda7d0 | 913 | static union acpi_hotkey *get_hotkey_by_event(struct |
4be44fcd LB |
914 | acpi_hotkey_list |
915 | *hotkey_list, int event) | |
fb9802fa | 916 | { |
79cda7d0 | 917 | struct list_head *entries; |
fb9802fa | 918 | |
79cda7d0 | 919 | list_for_each(entries, hotkey_list->entries) { |
fb9802fa LY |
920 | union acpi_hotkey *key = |
921 | container_of(entries, union acpi_hotkey, entries); | |
79cda7d0 | 922 | if (key->link.hotkey_standard_num == event) { |
4be44fcd | 923 | return (key); |
fb9802fa LY |
924 | } |
925 | } | |
4be44fcd | 926 | return (NULL); |
fb9802fa LY |
927 | } |
928 | ||
79cda7d0 | 929 | /* |
fb9802fa LY |
930 | * user call AML method interface: |
931 | * Call convention: | |
932 | * echo "event_num: arg type : value" | |
933 | * example: echo "1:1:30" > /proc/acpi/action | |
934 | * Just support 1 integer arg passing to AML method | |
935 | */ | |
936 | ||
937 | static ssize_t hotkey_execute_aml_method(struct file *file, | |
938 | const char __user * buffer, | |
939 | size_t count, loff_t * data) | |
940 | { | |
941 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
79cda7d0 | 942 | char *arg; |
4be44fcd | 943 | int event, method_type, type, value; |
79cda7d0 | 944 | union acpi_hotkey *key; |
fb9802fa LY |
945 | |
946 | ACPI_FUNCTION_TRACE("hotkey_execte_aml_method"); | |
947 | ||
4be44fcd LB |
948 | arg = (char *)kmalloc(count + 1, GFP_KERNEL); |
949 | if (!arg) | |
79cda7d0 | 950 | return_VALUE(-ENOMEM); |
4be44fcd | 951 | arg[count] = 0; |
fb9802fa LY |
952 | |
953 | if (copy_from_user(arg, buffer, count)) { | |
79cda7d0 | 954 | kfree(arg); |
fb9802fa LY |
955 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 2")); |
956 | return_VALUE(-EINVAL); | |
957 | } | |
958 | ||
4be44fcd LB |
959 | if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) != |
960 | 4) { | |
79cda7d0 | 961 | kfree(arg); |
fb9802fa LY |
962 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument 3")); |
963 | return_VALUE(-EINVAL); | |
964 | } | |
79cda7d0 | 965 | kfree(arg); |
fb9802fa | 966 | if (type == ACPI_TYPE_INTEGER) { |
79cda7d0 | 967 | key = get_hotkey_by_event(hotkey_list, event); |
4be44fcd | 968 | if (!key) |
79cda7d0 | 969 | goto do_fail; |
fb9802fa | 970 | if (IS_EVENT(event)) |
79cda7d0 | 971 | write_acpi_int(key->event_hotkey.action_handle, |
4be44fcd LB |
972 | key->event_hotkey.action_method, value, |
973 | NULL); | |
fb9802fa | 974 | else if (IS_POLL(event)) { |
4be44fcd | 975 | if (method_type == POLL_METHOD) |
79cda7d0 | 976 | read_acpi_int(key->poll_hotkey.poll_handle, |
4be44fcd LB |
977 | key->poll_hotkey.poll_method, |
978 | key->poll_hotkey.poll_result); | |
979 | else if (method_type == ACTION_METHOD) | |
79cda7d0 | 980 | write_acpi_int(key->poll_hotkey.action_handle, |
4be44fcd LB |
981 | key->poll_hotkey.action_method, |
982 | value, NULL); | |
79cda7d0 LY |
983 | else |
984 | goto do_fail; | |
985 | ||
fb9802fa LY |
986 | } |
987 | } else { | |
988 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Not supported")); | |
989 | return_VALUE(-EINVAL); | |
990 | } | |
fb9802fa | 991 | return_VALUE(count); |
4be44fcd | 992 | do_fail: |
79cda7d0 LY |
993 | return_VALUE(-EINVAL); |
994 | ||
fb9802fa LY |
995 | } |
996 | ||
997 | static int __init hotkey_init(void) | |
998 | { | |
999 | int result; | |
1000 | mode_t mode = S_IFREG | S_IRUGO | S_IWUGO; | |
1001 | ||
1002 | ACPI_FUNCTION_TRACE("hotkey_init"); | |
1003 | ||
1004 | if (acpi_disabled) | |
1005 | return -ENODEV; | |
1006 | ||
1007 | if (acpi_specific_hotkey_enabled) { | |
1008 | printk("Using specific hotkey driver\n"); | |
1009 | return -ENODEV; | |
1010 | } | |
1011 | ||
1012 | hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir); | |
1013 | if (!hotkey_proc_dir) { | |
1014 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1015 | "Hotkey: Unable to create %s entry\n", | |
1016 | HOTKEY_PROC)); | |
1017 | return (-ENODEV); | |
1018 | } | |
1019 | hotkey_proc_dir->owner = THIS_MODULE; | |
1020 | ||
1021 | hotkey_config = | |
1022 | create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir); | |
1023 | if (!hotkey_config) { | |
1024 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1025 | "Hotkey: Unable to create %s entry\n", | |
1026 | HOTKEY_EV_CONFIG)); | |
79cda7d0 | 1027 | goto do_fail1; |
fb9802fa LY |
1028 | } else { |
1029 | hotkey_config->proc_fops = &hotkey_config_fops; | |
1030 | hotkey_config->data = &global_hotkey_list; | |
1031 | hotkey_config->owner = THIS_MODULE; | |
1032 | hotkey_config->uid = 0; | |
1033 | hotkey_config->gid = 0; | |
1034 | } | |
1035 | ||
1036 | hotkey_poll_config = | |
1037 | create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir); | |
1038 | if (!hotkey_poll_config) { | |
1039 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1040 | "Hotkey: Unable to create %s entry\n", | |
1041 | HOTKEY_EV_CONFIG)); | |
79cda7d0 LY |
1042 | |
1043 | goto do_fail2; | |
fb9802fa LY |
1044 | } else { |
1045 | hotkey_poll_config->proc_fops = &hotkey_poll_config_fops; | |
1046 | hotkey_poll_config->data = &global_hotkey_list; | |
1047 | hotkey_poll_config->owner = THIS_MODULE; | |
1048 | hotkey_poll_config->uid = 0; | |
1049 | hotkey_poll_config->gid = 0; | |
1050 | } | |
1051 | ||
1052 | hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir); | |
1053 | if (!hotkey_action) { | |
1054 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1055 | "Hotkey: Unable to create %s entry\n", | |
1056 | HOTKEY_ACTION)); | |
79cda7d0 | 1057 | goto do_fail3; |
fb9802fa LY |
1058 | } else { |
1059 | hotkey_action->proc_fops = &hotkey_action_fops; | |
1060 | hotkey_action->owner = THIS_MODULE; | |
1061 | hotkey_action->uid = 0; | |
1062 | hotkey_action->gid = 0; | |
1063 | } | |
1064 | ||
1065 | hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir); | |
1066 | if (!hotkey_info) { | |
1067 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | |
1068 | "Hotkey: Unable to create %s entry\n", | |
1069 | HOTKEY_INFO)); | |
79cda7d0 | 1070 | goto do_fail4; |
fb9802fa LY |
1071 | } else { |
1072 | hotkey_info->proc_fops = &hotkey_info_fops; | |
1073 | hotkey_info->owner = THIS_MODULE; | |
1074 | hotkey_info->uid = 0; | |
1075 | hotkey_info->gid = 0; | |
1076 | } | |
1077 | ||
1078 | result = acpi_bus_register_driver(&hotkey_driver); | |
79cda7d0 LY |
1079 | if (result < 0) |
1080 | goto do_fail5; | |
fb9802fa LY |
1081 | global_hotkey_list.count = 0; |
1082 | global_hotkey_list.entries = &hotkey_entries; | |
1083 | ||
1084 | INIT_LIST_HEAD(&hotkey_entries); | |
1085 | ||
1086 | return (0); | |
79cda7d0 | 1087 | |
4be44fcd | 1088 | do_fail5: |
79cda7d0 | 1089 | remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); |
4be44fcd | 1090 | do_fail4: |
79cda7d0 | 1091 | remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); |
4be44fcd | 1092 | do_fail3: |
79cda7d0 | 1093 | remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); |
4be44fcd | 1094 | do_fail2: |
79cda7d0 | 1095 | remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); |
4be44fcd | 1096 | do_fail1: |
79cda7d0 LY |
1097 | remove_proc_entry(HOTKEY_PROC, acpi_root_dir); |
1098 | return (-ENODEV); | |
fb9802fa LY |
1099 | } |
1100 | ||
1101 | static void __exit hotkey_exit(void) | |
1102 | { | |
1103 | struct list_head *entries, *next; | |
1104 | ||
79cda7d0 | 1105 | ACPI_FUNCTION_TRACE("hotkey_exit"); |
fb9802fa LY |
1106 | |
1107 | list_for_each_safe(entries, next, global_hotkey_list.entries) { | |
1108 | union acpi_hotkey *key = | |
1109 | container_of(entries, union acpi_hotkey, entries); | |
1110 | ||
1111 | acpi_os_wait_events_complete(NULL); | |
1112 | list_del(&key->link.entries); | |
1113 | global_hotkey_list.count--; | |
1114 | free_hotkey_device(key); | |
1115 | } | |
1116 | acpi_bus_unregister_driver(&hotkey_driver); | |
1117 | remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); | |
1118 | remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); | |
1119 | remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); | |
1120 | remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); | |
1121 | remove_proc_entry(HOTKEY_PROC, acpi_root_dir); | |
1122 | return; | |
1123 | } | |
1124 | ||
1125 | module_init(hotkey_init); | |
1126 | module_exit(hotkey_exit); |