]>
Commit | Line | Data |
---|---|---|
79cda7d0 LY |
1 | /* |
2 | * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $) | |
fb9802fa LY |
3 | * |
4 | * Copyright (C) 2004 Luming Yu <[email protected]> | |
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("[email protected]"); |
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 | ||
5672bde6 H |
94 | enum conf_entry_enum { |
95 | bus_handle = 0, | |
96 | bus_method = 1, | |
97 | action_handle = 2, | |
98 | method = 3, | |
99 | LAST_CONF_ENTRY | |
100 | }; | |
101 | ||
fb9802fa LY |
102 | /* procdir we use */ |
103 | static struct proc_dir_entry *hotkey_proc_dir; | |
104 | static struct proc_dir_entry *hotkey_config; | |
105 | static struct proc_dir_entry *hotkey_poll_config; | |
106 | static struct proc_dir_entry *hotkey_action; | |
107 | static struct proc_dir_entry *hotkey_info; | |
108 | ||
109 | /* linkage for all type of hotkey */ | |
110 | struct acpi_hotkey_link { | |
111 | struct list_head entries; | |
112 | int hotkey_type; /* event or polling based hotkey */ | |
113 | int hotkey_standard_num; /* standardized hotkey(event) number */ | |
114 | }; | |
115 | ||
116 | /* event based hotkey */ | |
117 | struct acpi_event_hotkey { | |
118 | struct acpi_hotkey_link hotkey_link; | |
119 | int flag; | |
120 | acpi_handle bus_handle; /* bus to install notify handler */ | |
121 | int external_hotkey_num; /* external hotkey/event number */ | |
122 | acpi_handle action_handle; /* acpi handle attached aml action method */ | |
123 | char *action_method; /* action method */ | |
124 | }; | |
125 | ||
79cda7d0 | 126 | /* |
fb9802fa LY |
127 | * There are two ways to poll status |
128 | * 1. directy call read_xxx method, without any arguments passed in | |
129 | * 2. call write_xxx method, with arguments passed in, you need | |
130 | * the result is saved in acpi_polling_hotkey.poll_result. | |
131 | * anthoer read command through polling interface. | |
132 | * | |
133 | */ | |
134 | ||
135 | /* polling based hotkey */ | |
136 | struct acpi_polling_hotkey { | |
137 | struct acpi_hotkey_link hotkey_link; | |
138 | int flag; | |
139 | acpi_handle poll_handle; /* acpi handle attached polling method */ | |
140 | char *poll_method; /* poll method */ | |
141 | acpi_handle action_handle; /* acpi handle attached action method */ | |
142 | char *action_method; /* action method */ | |
79cda7d0 | 143 | union acpi_object *poll_result; /* polling_result */ |
fb9802fa LY |
144 | struct proc_dir_entry *proc; |
145 | }; | |
146 | ||
147 | /* hotkey object union */ | |
148 | union acpi_hotkey { | |
149 | struct list_head entries; | |
150 | struct acpi_hotkey_link link; | |
151 | struct acpi_event_hotkey event_hotkey; | |
152 | struct acpi_polling_hotkey poll_hotkey; | |
153 | }; | |
154 | ||
155 | /* hotkey object list */ | |
156 | struct acpi_hotkey_list { | |
157 | struct list_head *entries; | |
158 | int count; | |
159 | }; | |
160 | ||
161 | static int auto_hotkey_add(struct acpi_device *device); | |
162 | static int auto_hotkey_remove(struct acpi_device *device, int type); | |
163 | ||
164 | static struct acpi_driver hotkey_driver = { | |
165 | .name = ACPI_HOTK_NAME, | |
166 | .class = ACPI_HOTK_CLASS, | |
167 | .ids = ACPI_HOTK_HID, | |
168 | .ops = { | |
169 | .add = auto_hotkey_add, | |
170 | .remove = auto_hotkey_remove, | |
171 | }, | |
172 | }; | |
173 | ||
79cda7d0 LY |
174 | static void free_hotkey_device(union acpi_hotkey *key); |
175 | static void free_hotkey_buffer(union acpi_hotkey *key); | |
176 | static void free_poll_hotkey_buffer(union acpi_hotkey *key); | |
fb9802fa | 177 | static int hotkey_open_config(struct inode *inode, struct file *file); |
79cda7d0 | 178 | static int hotkey_poll_open_config(struct inode *inode, struct file *file); |
fb9802fa LY |
179 | static ssize_t hotkey_write_config(struct file *file, |
180 | const char __user * buffer, | |
181 | size_t count, loff_t * data); | |
fb9802fa LY |
182 | static int hotkey_info_open_fs(struct inode *inode, struct file *file); |
183 | static int hotkey_action_open_fs(struct inode *inode, struct file *file); | |
184 | static ssize_t hotkey_execute_aml_method(struct file *file, | |
185 | const char __user * buffer, | |
186 | size_t count, loff_t * data); | |
187 | static int hotkey_config_seq_show(struct seq_file *seq, void *offset); | |
79cda7d0 | 188 | static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset); |
fb9802fa | 189 | static int hotkey_polling_open_fs(struct inode *inode, struct file *file); |
79cda7d0 | 190 | static union acpi_hotkey *get_hotkey_by_event(struct |
4be44fcd LB |
191 | acpi_hotkey_list |
192 | *hotkey_list, int event); | |
fb9802fa LY |
193 | |
194 | /* event based config */ | |
d7508032 | 195 | static const struct file_operations hotkey_config_fops = { |
fb9802fa LY |
196 | .open = hotkey_open_config, |
197 | .read = seq_read, | |
198 | .write = hotkey_write_config, | |
199 | .llseek = seq_lseek, | |
200 | .release = single_release, | |
201 | }; | |
202 | ||
203 | /* polling based config */ | |
d7508032 | 204 | static const struct file_operations hotkey_poll_config_fops = { |
79cda7d0 | 205 | .open = hotkey_poll_open_config, |
fb9802fa | 206 | .read = seq_read, |
79cda7d0 | 207 | .write = hotkey_write_config, |
fb9802fa LY |
208 | .llseek = seq_lseek, |
209 | .release = single_release, | |
210 | }; | |
211 | ||
212 | /* hotkey driver info */ | |
d7508032 | 213 | static const struct file_operations hotkey_info_fops = { |
fb9802fa LY |
214 | .open = hotkey_info_open_fs, |
215 | .read = seq_read, | |
216 | .llseek = seq_lseek, | |
217 | .release = single_release, | |
218 | }; | |
219 | ||
220 | /* action */ | |
d7508032 | 221 | static const struct file_operations hotkey_action_fops = { |
fb9802fa LY |
222 | .open = hotkey_action_open_fs, |
223 | .read = seq_read, | |
224 | .write = hotkey_execute_aml_method, | |
225 | .llseek = seq_lseek, | |
226 | .release = single_release, | |
227 | }; | |
228 | ||
229 | /* polling results */ | |
d7508032 | 230 | static const struct file_operations hotkey_polling_fops = { |
fb9802fa LY |
231 | .open = hotkey_polling_open_fs, |
232 | .read = seq_read, | |
233 | .llseek = seq_lseek, | |
234 | .release = single_release, | |
235 | }; | |
236 | ||
237 | struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */ | |
238 | struct list_head hotkey_entries; /* head of the list of hotkey_list */ | |
239 | ||
240 | static int hotkey_info_seq_show(struct seq_file *seq, void *offset) | |
241 | { | |
fb9802fa | 242 | |
79cda7d0 | 243 | seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION); |
fb9802fa | 244 | |
d550d98d | 245 | return 0; |
fb9802fa LY |
246 | } |
247 | ||
248 | static int hotkey_info_open_fs(struct inode *inode, struct file *file) | |
249 | { | |
250 | return single_open(file, hotkey_info_seq_show, PDE(inode)->data); | |
251 | } | |
252 | ||
253 | static char *format_result(union acpi_object *object) | |
254 | { | |
5672bde6 | 255 | char *buf; |
fb9802fa | 256 | |
5672bde6 H |
257 | buf = kzalloc(RESULT_STR_LEN, GFP_KERNEL); |
258 | if (!buf) | |
259 | return NULL; | |
fb9802fa LY |
260 | /* Now, just support integer type */ |
261 | if (object->type == ACPI_TYPE_INTEGER) | |
79cda7d0 | 262 | sprintf(buf, "%d\n", (u32) object->integer.value); |
5672bde6 | 263 | return buf; |
fb9802fa LY |
264 | } |
265 | ||
266 | static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) | |
267 | { | |
268 | struct acpi_polling_hotkey *poll_hotkey = | |
269 | (struct acpi_polling_hotkey *)seq->private; | |
79cda7d0 | 270 | char *buf; |
fb9802fa | 271 | |
fb9802fa | 272 | |
4be44fcd | 273 | if (poll_hotkey->poll_result) { |
79cda7d0 | 274 | buf = format_result(poll_hotkey->poll_result); |
4be44fcd | 275 | if (buf) |
79cda7d0 LY |
276 | seq_printf(seq, "%s", buf); |
277 | kfree(buf); | |
278 | } | |
d550d98d | 279 | return 0; |
fb9802fa LY |
280 | } |
281 | ||
282 | static int hotkey_polling_open_fs(struct inode *inode, struct file *file) | |
283 | { | |
284 | return single_open(file, hotkey_polling_seq_show, PDE(inode)->data); | |
285 | } | |
286 | ||
287 | static int hotkey_action_open_fs(struct inode *inode, struct file *file) | |
288 | { | |
289 | return single_open(file, hotkey_info_seq_show, PDE(inode)->data); | |
290 | } | |
291 | ||
292 | /* Mapping external hotkey number to standardized hotkey event num */ | |
293 | static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list) | |
294 | { | |
79cda7d0 LY |
295 | struct list_head *entries; |
296 | int val = -1; | |
fb9802fa | 297 | |
fb9802fa | 298 | |
79cda7d0 | 299 | list_for_each(entries, list->entries) { |
fb9802fa LY |
300 | union acpi_hotkey *key = |
301 | container_of(entries, union acpi_hotkey, entries); | |
302 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT | |
4be44fcd | 303 | && key->event_hotkey.external_hotkey_num == event) { |
fb9802fa | 304 | val = key->link.hotkey_standard_num; |
79cda7d0 LY |
305 | break; |
306 | } | |
fb9802fa LY |
307 | } |
308 | ||
d550d98d | 309 | return val; |
fb9802fa LY |
310 | } |
311 | ||
312 | static void | |
313 | acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data) | |
314 | { | |
315 | struct acpi_device *device = NULL; | |
316 | u32 internal_event; | |
317 | ||
fb9802fa LY |
318 | |
319 | if (acpi_bus_get_device(handle, &device)) | |
d550d98d | 320 | return; |
fb9802fa LY |
321 | |
322 | internal_event = hotkey_get_internal_event(event, &global_hotkey_list); | |
79cda7d0 | 323 | acpi_bus_generate_event(device, internal_event, 0); |
fb9802fa | 324 | |
d550d98d | 325 | return; |
fb9802fa LY |
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 | 348 | |
8de7a63b | 349 | mode = S_IFREG | S_IRUGO | S_IWUGO; |
fb9802fa | 350 | |
79cda7d0 LY |
351 | sprintf(proc_name, "%d", device->link.hotkey_standard_num); |
352 | /* | |
4be44fcd LB |
353 | strcat(proc_name, device->poll_hotkey.poll_method); |
354 | */ | |
79cda7d0 | 355 | proc = create_proc_entry(proc_name, mode, hotkey_proc_dir); |
fb9802fa LY |
356 | |
357 | if (!proc) { | |
d550d98d | 358 | return -ENODEV; |
fb9802fa LY |
359 | } else { |
360 | proc->proc_fops = &hotkey_polling_fops; | |
361 | proc->owner = THIS_MODULE; | |
362 | proc->data = device; | |
363 | proc->uid = 0; | |
364 | proc->gid = 0; | |
365 | device->poll_hotkey.proc = proc; | |
366 | } | |
d550d98d | 367 | return 0; |
fb9802fa LY |
368 | } |
369 | ||
fb9802fa LY |
370 | static int hotkey_add(union acpi_hotkey *device) |
371 | { | |
372 | int status = 0; | |
373 | struct acpi_device *dev = NULL; | |
374 | ||
fb9802fa LY |
375 | |
376 | if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
79cda7d0 | 377 | acpi_bus_get_device(device->event_hotkey.bus_handle, &dev); |
fb9802fa | 378 | status = acpi_install_notify_handler(dev->handle, |
79cda7d0 | 379 | ACPI_DEVICE_NOTIFY, |
fb9802fa | 380 | acpi_hotkey_notify_handler, |
79cda7d0 | 381 | dev); |
fb9802fa LY |
382 | } else /* Add polling hotkey */ |
383 | create_polling_proc(device); | |
384 | ||
385 | global_hotkey_list.count++; | |
386 | ||
387 | list_add_tail(&device->link.entries, global_hotkey_list.entries); | |
388 | ||
d550d98d | 389 | return status; |
fb9802fa LY |
390 | } |
391 | ||
392 | static int hotkey_remove(union acpi_hotkey *device) | |
393 | { | |
394 | struct list_head *entries, *next; | |
395 | ||
fb9802fa LY |
396 | |
397 | list_for_each_safe(entries, next, global_hotkey_list.entries) { | |
398 | union acpi_hotkey *key = | |
399 | container_of(entries, union acpi_hotkey, entries); | |
400 | if (key->link.hotkey_standard_num == | |
401 | device->link.hotkey_standard_num) { | |
402 | list_del(&key->link.entries); | |
79cda7d0 | 403 | free_hotkey_device(key); |
fb9802fa LY |
404 | global_hotkey_list.count--; |
405 | break; | |
406 | } | |
407 | } | |
79cda7d0 | 408 | kfree(device); |
d550d98d | 409 | return 0; |
fb9802fa LY |
410 | } |
411 | ||
4be44fcd | 412 | static int hotkey_update(union acpi_hotkey *key) |
fb9802fa | 413 | { |
79cda7d0 | 414 | struct list_head *entries; |
fb9802fa | 415 | |
fb9802fa | 416 | |
79cda7d0 | 417 | list_for_each(entries, global_hotkey_list.entries) { |
4be44fcd | 418 | union acpi_hotkey *tmp = |
fb9802fa | 419 | container_of(entries, union acpi_hotkey, entries); |
79cda7d0 | 420 | if (tmp->link.hotkey_standard_num == |
fb9802fa | 421 | key->link.hotkey_standard_num) { |
79cda7d0 LY |
422 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { |
423 | free_hotkey_buffer(tmp); | |
424 | tmp->event_hotkey.bus_handle = | |
4be44fcd | 425 | key->event_hotkey.bus_handle; |
79cda7d0 | 426 | tmp->event_hotkey.external_hotkey_num = |
4be44fcd | 427 | key->event_hotkey.external_hotkey_num; |
79cda7d0 | 428 | tmp->event_hotkey.action_handle = |
4be44fcd | 429 | key->event_hotkey.action_handle; |
79cda7d0 | 430 | tmp->event_hotkey.action_method = |
4be44fcd | 431 | key->event_hotkey.action_method; |
79cda7d0 LY |
432 | kfree(key); |
433 | } else { | |
434 | /* | |
4be44fcd | 435 | char proc_name[80]; |
79cda7d0 | 436 | |
4be44fcd LB |
437 | sprintf(proc_name, "%d", tmp->link.hotkey_standard_num); |
438 | strcat(proc_name, tmp->poll_hotkey.poll_method); | |
439 | remove_proc_entry(proc_name,hotkey_proc_dir); | |
440 | */ | |
79cda7d0 LY |
441 | free_poll_hotkey_buffer(tmp); |
442 | tmp->poll_hotkey.poll_handle = | |
4be44fcd | 443 | key->poll_hotkey.poll_handle; |
79cda7d0 | 444 | tmp->poll_hotkey.poll_method = |
4be44fcd | 445 | key->poll_hotkey.poll_method; |
79cda7d0 | 446 | tmp->poll_hotkey.action_handle = |
4be44fcd | 447 | key->poll_hotkey.action_handle; |
79cda7d0 | 448 | tmp->poll_hotkey.action_method = |
4be44fcd | 449 | key->poll_hotkey.action_method; |
79cda7d0 | 450 | tmp->poll_hotkey.poll_result = |
4be44fcd | 451 | key->poll_hotkey.poll_result; |
79cda7d0 | 452 | /* |
4be44fcd LB |
453 | create_polling_proc(tmp); |
454 | */ | |
79cda7d0 LY |
455 | kfree(key); |
456 | } | |
d550d98d | 457 | return 0; |
fb9802fa LY |
458 | break; |
459 | } | |
460 | } | |
461 | ||
d550d98d | 462 | return -ENODEV; |
fb9802fa LY |
463 | } |
464 | ||
465 | static void free_hotkey_device(union acpi_hotkey *key) | |
466 | { | |
467 | struct acpi_device *dev; | |
fb9802fa | 468 | |
fb9802fa LY |
469 | |
470 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
79cda7d0 | 471 | acpi_bus_get_device(key->event_hotkey.bus_handle, &dev); |
fb9802fa LY |
472 | if (dev->handle) |
473 | acpi_remove_notify_handler(dev->handle, | |
79cda7d0 | 474 | ACPI_DEVICE_NOTIFY, |
fb9802fa | 475 | acpi_hotkey_notify_handler); |
79cda7d0 LY |
476 | free_hotkey_buffer(key); |
477 | } else { | |
4be44fcd | 478 | char proc_name[80]; |
79cda7d0 LY |
479 | |
480 | sprintf(proc_name, "%d", key->link.hotkey_standard_num); | |
481 | /* | |
4be44fcd LB |
482 | strcat(proc_name, key->poll_hotkey.poll_method); |
483 | */ | |
484 | remove_proc_entry(proc_name, hotkey_proc_dir); | |
79cda7d0 LY |
485 | free_poll_hotkey_buffer(key); |
486 | } | |
fb9802fa | 487 | kfree(key); |
d550d98d | 488 | return; |
fb9802fa LY |
489 | } |
490 | ||
4be44fcd | 491 | static void free_hotkey_buffer(union acpi_hotkey *key) |
79cda7d0 | 492 | { |
5672bde6 | 493 | /* key would never be null, action method could be */ |
79cda7d0 LY |
494 | kfree(key->event_hotkey.action_method); |
495 | } | |
496 | ||
4be44fcd | 497 | static void free_poll_hotkey_buffer(union acpi_hotkey *key) |
79cda7d0 | 498 | { |
5672bde6 | 499 | /* key would never be null, others could be*/ |
79cda7d0 LY |
500 | kfree(key->poll_hotkey.action_method); |
501 | kfree(key->poll_hotkey.poll_method); | |
502 | kfree(key->poll_hotkey.poll_result); | |
503 | } | |
fb9802fa | 504 | static int |
5672bde6 H |
505 | init_hotkey_device(union acpi_hotkey *key, char **config_entry, |
506 | int std_num, int external_num) | |
fb9802fa | 507 | { |
4be44fcd | 508 | acpi_handle tmp_handle; |
79cda7d0 LY |
509 | acpi_status status = AE_OK; |
510 | ||
4be44fcd | 511 | if (std_num < 0 || IS_POLL(std_num) || !key) |
79cda7d0 LY |
512 | goto do_fail; |
513 | ||
5672bde6 H |
514 | if (!config_entry[bus_handle] || !config_entry[action_handle] |
515 | || !config_entry[method]) | |
79cda7d0 LY |
516 | goto do_fail; |
517 | ||
fb9802fa LY |
518 | key->link.hotkey_type = ACPI_HOTKEY_EVENT; |
519 | key->link.hotkey_standard_num = std_num; | |
520 | key->event_hotkey.flag = 0; | |
5672bde6 | 521 | key->event_hotkey.action_method = config_entry[method]; |
fb9802fa | 522 | |
5672bde6 H |
523 | status = acpi_get_handle(NULL, config_entry[bus_handle], |
524 | &(key->event_hotkey.bus_handle)); | |
4be44fcd | 525 | if (ACPI_FAILURE(status)) |
5672bde6 | 526 | goto do_fail_zero; |
79cda7d0 | 527 | key->event_hotkey.external_hotkey_num = external_num; |
5672bde6 | 528 | status = acpi_get_handle(NULL, config_entry[action_handle], |
4be44fcd LB |
529 | &(key->event_hotkey.action_handle)); |
530 | if (ACPI_FAILURE(status)) | |
5672bde6 | 531 | goto do_fail_zero; |
79cda7d0 | 532 | status = acpi_get_handle(key->event_hotkey.action_handle, |
5672bde6 | 533 | config_entry[method], &tmp_handle); |
79cda7d0 | 534 | if (ACPI_FAILURE(status)) |
5672bde6 | 535 | goto do_fail_zero; |
d550d98d | 536 | return AE_OK; |
5672bde6 H |
537 | do_fail_zero: |
538 | key->event_hotkey.action_method = NULL; | |
539 | do_fail: | |
d550d98d | 540 | return -ENODEV; |
fb9802fa LY |
541 | } |
542 | ||
543 | static int | |
5672bde6 H |
544 | init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry, |
545 | int std_num) | |
fb9802fa | 546 | { |
79cda7d0 | 547 | acpi_status status = AE_OK; |
4be44fcd | 548 | acpi_handle tmp_handle; |
79cda7d0 | 549 | |
4be44fcd | 550 | if (std_num < 0 || IS_EVENT(std_num) || !key) |
79cda7d0 | 551 | goto do_fail; |
5672bde6 H |
552 | if (!config_entry[bus_handle] ||!config_entry[bus_method] || |
553 | !config_entry[action_handle] || !config_entry[method]) | |
79cda7d0 LY |
554 | goto do_fail; |
555 | ||
fb9802fa LY |
556 | key->link.hotkey_type = ACPI_HOTKEY_POLLING; |
557 | key->link.hotkey_standard_num = std_num; | |
558 | key->poll_hotkey.flag = 0; | |
5672bde6 H |
559 | key->poll_hotkey.poll_method = config_entry[bus_method]; |
560 | key->poll_hotkey.action_method = config_entry[method]; | |
79cda7d0 | 561 | |
5672bde6 H |
562 | status = acpi_get_handle(NULL, config_entry[bus_handle], |
563 | &(key->poll_hotkey.poll_handle)); | |
4be44fcd | 564 | if (ACPI_FAILURE(status)) |
5672bde6 | 565 | goto do_fail_zero; |
79cda7d0 | 566 | status = acpi_get_handle(key->poll_hotkey.poll_handle, |
5672bde6 | 567 | config_entry[bus_method], &tmp_handle); |
4be44fcd | 568 | if (ACPI_FAILURE(status)) |
5672bde6 | 569 | goto do_fail_zero; |
4be44fcd | 570 | status = |
5672bde6 | 571 | acpi_get_handle(NULL, config_entry[action_handle], |
4be44fcd | 572 | &(key->poll_hotkey.action_handle)); |
79cda7d0 | 573 | if (ACPI_FAILURE(status)) |
5672bde6 | 574 | goto do_fail_zero; |
79cda7d0 | 575 | status = acpi_get_handle(key->poll_hotkey.action_handle, |
5672bde6 | 576 | config_entry[method], &tmp_handle); |
79cda7d0 | 577 | if (ACPI_FAILURE(status)) |
5672bde6 | 578 | goto do_fail_zero; |
fb9802fa LY |
579 | key->poll_hotkey.poll_result = |
580 | (union acpi_object *)kmalloc(sizeof(union acpi_object), GFP_KERNEL); | |
4be44fcd | 581 | if (!key->poll_hotkey.poll_result) |
5672bde6 | 582 | goto do_fail_zero; |
d550d98d | 583 | return AE_OK; |
5672bde6 H |
584 | |
585 | do_fail_zero: | |
586 | key->poll_hotkey.poll_method = NULL; | |
587 | key->poll_hotkey.action_method = NULL; | |
588 | do_fail: | |
d550d98d | 589 | return -ENODEV; |
fb9802fa LY |
590 | } |
591 | ||
fb9802fa LY |
592 | static int hotkey_open_config(struct inode *inode, struct file *file) |
593 | { | |
d550d98d | 594 | return (single_open |
fb9802fa LY |
595 | (file, hotkey_config_seq_show, PDE(inode)->data)); |
596 | } | |
597 | ||
79cda7d0 LY |
598 | static int hotkey_poll_open_config(struct inode *inode, struct file *file) |
599 | { | |
d550d98d | 600 | return (single_open |
79cda7d0 LY |
601 | (file, hotkey_poll_config_seq_show, PDE(inode)->data)); |
602 | } | |
603 | ||
fb9802fa LY |
604 | static int hotkey_config_seq_show(struct seq_file *seq, void *offset) |
605 | { | |
606 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
79cda7d0 | 607 | struct list_head *entries; |
fb9802fa LY |
608 | char bus_name[ACPI_PATHNAME_MAX] = { 0 }; |
609 | char action_name[ACPI_PATHNAME_MAX] = { 0 }; | |
610 | struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; | |
611 | struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; | |
612 | ||
fb9802fa | 613 | |
79cda7d0 | 614 | list_for_each(entries, hotkey_list->entries) { |
fb9802fa LY |
615 | union acpi_hotkey *key = |
616 | container_of(entries, union acpi_hotkey, entries); | |
617 | if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { | |
618 | acpi_get_name(key->event_hotkey.bus_handle, | |
619 | ACPI_NAME_TYPE_MAX, &bus); | |
620 | acpi_get_name(key->event_hotkey.action_handle, | |
621 | ACPI_NAME_TYPE_MAX, &act); | |
79cda7d0 | 622 | seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name, |
fb9802fa LY |
623 | action_name, |
624 | key->event_hotkey.action_method, | |
625 | key->link.hotkey_standard_num, | |
626 | key->event_hotkey.external_hotkey_num); | |
79cda7d0 LY |
627 | } |
628 | } | |
629 | seq_puts(seq, "\n"); | |
d550d98d | 630 | return 0; |
79cda7d0 LY |
631 | } |
632 | ||
633 | static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset) | |
634 | { | |
635 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
636 | struct list_head *entries; | |
637 | char bus_name[ACPI_PATHNAME_MAX] = { 0 }; | |
638 | char action_name[ACPI_PATHNAME_MAX] = { 0 }; | |
639 | struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; | |
640 | struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; | |
641 | ||
79cda7d0 LY |
642 | |
643 | list_for_each(entries, hotkey_list->entries) { | |
644 | union acpi_hotkey *key = | |
645 | container_of(entries, union acpi_hotkey, entries); | |
646 | if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) { | |
fb9802fa LY |
647 | acpi_get_name(key->poll_hotkey.poll_handle, |
648 | ACPI_NAME_TYPE_MAX, &bus); | |
649 | acpi_get_name(key->poll_hotkey.action_handle, | |
650 | ACPI_NAME_TYPE_MAX, &act); | |
79cda7d0 | 651 | seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name, |
fb9802fa LY |
652 | key->poll_hotkey.poll_method, |
653 | action_name, | |
654 | key->poll_hotkey.action_method, | |
655 | key->link.hotkey_standard_num); | |
656 | } | |
657 | } | |
658 | seq_puts(seq, "\n"); | |
d550d98d | 659 | return 0; |
fb9802fa LY |
660 | } |
661 | ||
662 | static int | |
5672bde6 H |
663 | get_parms(char *config_record, int *cmd, char **config_entry, |
664 | int *internal_event_num, int *external_event_num) | |
fb9802fa | 665 | { |
5672bde6 H |
666 | /* the format of *config_record = |
667 | * "1:\d+:*" : "cmd:internal_event_num" | |
668 | * "\d+:\w+:\w+:\w+:\w+:\d+:\d+" : | |
669 | * "cmd:bus_handle:bus_method:action_handle:method:internal_event_num:external_event_num" | |
670 | */ | |
79cda7d0 | 671 | char *tmp, *tmp1, count; |
5672bde6 | 672 | int i; |
fb9802fa LY |
673 | |
674 | sscanf(config_record, "%d", cmd); | |
4be44fcd LB |
675 | if (*cmd == 1) { |
676 | if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != | |
677 | 2) | |
79cda7d0 LY |
678 | goto do_fail; |
679 | else | |
680 | return (6); | |
681 | } | |
fb9802fa | 682 | tmp = strchr(config_record, ':'); |
79cda7d0 LY |
683 | if (!tmp) |
684 | goto do_fail; | |
fb9802fa | 685 | tmp++; |
5672bde6 H |
686 | for (i = 0; i < LAST_CONF_ENTRY; i++) { |
687 | tmp1 = strchr(tmp, ':'); | |
688 | if (!tmp1) { | |
689 | goto do_fail; | |
690 | } | |
691 | count = tmp1 - tmp; | |
692 | config_entry[i] = kzalloc(count + 1, GFP_KERNEL); | |
693 | if (!config_entry[i]) | |
694 | goto handle_failure; | |
695 | strncpy(config_entry[i], tmp, count); | |
696 | tmp = tmp1 + 1; | |
697 | } | |
698 | if (sscanf(tmp, "%d:%d", internal_event_num, external_event_num) <= 0) | |
699 | goto handle_failure; | |
700 | if (!IS_OTHERS(*internal_event_num)) { | |
701 | return 6; | |
702 | } | |
703 | handle_failure: | |
704 | while (i-- > 0) | |
705 | kfree(config_entry[i]); | |
706 | do_fail: | |
d550d98d | 707 | return -1; |
fb9802fa LY |
708 | } |
709 | ||
710 | /* count is length for one input record */ | |
711 | static ssize_t hotkey_write_config(struct file *file, | |
712 | const char __user * buffer, | |
713 | size_t count, loff_t * data) | |
714 | { | |
79cda7d0 | 715 | char *config_record = NULL; |
5672bde6 | 716 | char *config_entry[LAST_CONF_ENTRY]; |
fb9802fa LY |
717 | int cmd, internal_event_num, external_event_num; |
718 | int ret = 0; | |
5672bde6 | 719 | union acpi_hotkey *key = kzalloc(sizeof(union acpi_hotkey), GFP_KERNEL); |
fb9802fa | 720 | |
5672bde6 H |
721 | if (!key) |
722 | return -ENOMEM; | |
fb9802fa | 723 | |
5672bde6 H |
724 | config_record = kzalloc(count + 1, GFP_KERNEL); |
725 | if (!config_record) { | |
726 | kfree(key); | |
d550d98d | 727 | return -ENOMEM; |
5672bde6 | 728 | } |
fb9802fa LY |
729 | |
730 | if (copy_from_user(config_record, buffer, count)) { | |
79cda7d0 | 731 | kfree(config_record); |
5672bde6 | 732 | kfree(key); |
6468463a | 733 | printk(KERN_ERR PREFIX "Invalid data\n"); |
d550d98d | 734 | return -EINVAL; |
fb9802fa | 735 | } |
5672bde6 H |
736 | ret = get_parms(config_record, &cmd, config_entry, |
737 | &internal_event_num, &external_event_num); | |
79cda7d0 | 738 | kfree(config_record); |
fb9802fa | 739 | if (ret != 6) { |
6468463a | 740 | printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret); |
d550d98d | 741 | return -EINVAL; |
fb9802fa LY |
742 | } |
743 | ||
4be44fcd | 744 | if (cmd == 1) { |
79cda7d0 LY |
745 | union acpi_hotkey *tmp = NULL; |
746 | tmp = get_hotkey_by_event(&global_hotkey_list, | |
4be44fcd LB |
747 | internal_event_num); |
748 | if (!tmp) | |
6468463a | 749 | printk(KERN_ERR PREFIX "Invalid key\n"); |
79cda7d0 LY |
750 | else |
751 | memcpy(key, tmp, sizeof(union acpi_hotkey)); | |
752 | goto cont_cmd; | |
753 | } | |
754 | if (IS_EVENT(internal_event_num)) { | |
5672bde6 H |
755 | if (init_hotkey_device(key, config_entry, |
756 | internal_event_num, external_event_num)) | |
757 | goto init_hotkey_fail; | |
758 | } else { | |
759 | if (init_poll_hotkey_device(key, config_entry, | |
760 | internal_event_num)) | |
761 | goto init_poll_hotkey_fail; | |
fb9802fa | 762 | } |
5672bde6 | 763 | cont_cmd: |
fb9802fa LY |
764 | switch (cmd) { |
765 | case 0: | |
5672bde6 H |
766 | if (get_hotkey_by_event(&global_hotkey_list, |
767 | key->link.hotkey_standard_num)) | |
79cda7d0 LY |
768 | goto fail_out; |
769 | else | |
770 | hotkey_add(key); | |
fb9802fa LY |
771 | break; |
772 | case 1: | |
773 | hotkey_remove(key); | |
774 | break; | |
775 | case 2: | |
5672bde6 | 776 | /* key is kfree()ed if matched*/ |
4be44fcd | 777 | if (hotkey_update(key)) |
79cda7d0 | 778 | goto fail_out; |
fb9802fa LY |
779 | break; |
780 | default: | |
79cda7d0 | 781 | goto fail_out; |
fb9802fa LY |
782 | break; |
783 | } | |
d550d98d | 784 | return count; |
5672bde6 H |
785 | |
786 | init_poll_hotkey_fail: /* failed init_poll_hotkey_device */ | |
787 | kfree(config_entry[bus_method]); | |
788 | config_entry[bus_method] = NULL; | |
789 | init_hotkey_fail: /* failed init_hotkey_device */ | |
790 | kfree(config_entry[method]); | |
791 | fail_out: | |
792 | kfree(config_entry[bus_handle]); | |
793 | kfree(config_entry[action_handle]); | |
794 | /* No double free since elements =NULL for error cases */ | |
795 | if (IS_EVENT(internal_event_num)) { | |
796 | if (config_entry[bus_method]) | |
797 | kfree(config_entry[bus_method]); | |
798 | free_hotkey_buffer(key); /* frees [method] */ | |
799 | } else | |
800 | free_poll_hotkey_buffer(key); /* frees [bus_method]+[method] */ | |
79cda7d0 | 801 | kfree(key); |
6468463a | 802 | printk(KERN_ERR PREFIX "invalid key\n"); |
d550d98d | 803 | return -EINVAL; |
fb9802fa LY |
804 | } |
805 | ||
79cda7d0 | 806 | /* |
fb9802fa LY |
807 | * This function evaluates an ACPI method, given an int as parameter, the |
808 | * method is searched within the scope of the handle, can be NULL. The output | |
809 | * of the method is written is output, which can also be NULL | |
810 | * | |
811 | * returns 1 if write is successful, 0 else. | |
812 | */ | |
813 | static int write_acpi_int(acpi_handle handle, const char *method, int val, | |
814 | struct acpi_buffer *output) | |
815 | { | |
816 | struct acpi_object_list params; /* list of input parameters (an int here) */ | |
817 | union acpi_object in_obj; /* the only param we use */ | |
818 | acpi_status status; | |
819 | ||
fb9802fa LY |
820 | params.count = 1; |
821 | params.pointer = &in_obj; | |
822 | in_obj.type = ACPI_TYPE_INTEGER; | |
823 | in_obj.integer.value = val; | |
824 | ||
825 | status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); | |
826 | ||
d550d98d | 827 | return (status == AE_OK); |
fb9802fa LY |
828 | } |
829 | ||
4be44fcd LB |
830 | static int read_acpi_int(acpi_handle handle, const char *method, |
831 | union acpi_object *val) | |
fb9802fa LY |
832 | { |
833 | struct acpi_buffer output; | |
834 | union acpi_object out_obj; | |
835 | acpi_status status; | |
836 | ||
fb9802fa LY |
837 | output.length = sizeof(out_obj); |
838 | output.pointer = &out_obj; | |
839 | ||
840 | status = acpi_evaluate_object(handle, (char *)method, NULL, &output); | |
4be44fcd | 841 | if (val) { |
79cda7d0 LY |
842 | val->integer.value = out_obj.integer.value; |
843 | val->type = out_obj.type; | |
844 | } else | |
6468463a | 845 | printk(KERN_ERR PREFIX "null val pointer\n"); |
d550d98d | 846 | return ((status == AE_OK) |
fb9802fa LY |
847 | && (out_obj.type == ACPI_TYPE_INTEGER)); |
848 | } | |
849 | ||
79cda7d0 | 850 | static union acpi_hotkey *get_hotkey_by_event(struct |
4be44fcd LB |
851 | acpi_hotkey_list |
852 | *hotkey_list, int event) | |
fb9802fa | 853 | { |
79cda7d0 | 854 | struct list_head *entries; |
fb9802fa | 855 | |
79cda7d0 | 856 | list_for_each(entries, hotkey_list->entries) { |
fb9802fa LY |
857 | union acpi_hotkey *key = |
858 | container_of(entries, union acpi_hotkey, entries); | |
79cda7d0 | 859 | if (key->link.hotkey_standard_num == event) { |
4be44fcd | 860 | return (key); |
fb9802fa LY |
861 | } |
862 | } | |
4be44fcd | 863 | return (NULL); |
fb9802fa LY |
864 | } |
865 | ||
79cda7d0 | 866 | /* |
fb9802fa LY |
867 | * user call AML method interface: |
868 | * Call convention: | |
869 | * echo "event_num: arg type : value" | |
870 | * example: echo "1:1:30" > /proc/acpi/action | |
871 | * Just support 1 integer arg passing to AML method | |
872 | */ | |
873 | ||
874 | static ssize_t hotkey_execute_aml_method(struct file *file, | |
875 | const char __user * buffer, | |
876 | size_t count, loff_t * data) | |
877 | { | |
878 | struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; | |
79cda7d0 | 879 | char *arg; |
4be44fcd | 880 | int event, method_type, type, value; |
79cda7d0 | 881 | union acpi_hotkey *key; |
fb9802fa | 882 | |
fb9802fa | 883 | |
5672bde6 | 884 | arg = kzalloc(count + 1, GFP_KERNEL); |
4be44fcd | 885 | if (!arg) |
d550d98d | 886 | return -ENOMEM; |
fb9802fa LY |
887 | |
888 | if (copy_from_user(arg, buffer, count)) { | |
79cda7d0 | 889 | kfree(arg); |
6468463a | 890 | printk(KERN_ERR PREFIX "Invalid argument 2\n"); |
d550d98d | 891 | return -EINVAL; |
fb9802fa LY |
892 | } |
893 | ||
4be44fcd LB |
894 | if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) != |
895 | 4) { | |
79cda7d0 | 896 | kfree(arg); |
6468463a | 897 | printk(KERN_ERR PREFIX "Invalid argument 3\n"); |
d550d98d | 898 | return -EINVAL; |
fb9802fa | 899 | } |
79cda7d0 | 900 | kfree(arg); |
fb9802fa | 901 | if (type == ACPI_TYPE_INTEGER) { |
79cda7d0 | 902 | key = get_hotkey_by_event(hotkey_list, event); |
4be44fcd | 903 | if (!key) |
79cda7d0 | 904 | goto do_fail; |
fb9802fa | 905 | if (IS_EVENT(event)) |
79cda7d0 | 906 | write_acpi_int(key->event_hotkey.action_handle, |
4be44fcd LB |
907 | key->event_hotkey.action_method, value, |
908 | NULL); | |
fb9802fa | 909 | else if (IS_POLL(event)) { |
4be44fcd | 910 | if (method_type == POLL_METHOD) |
79cda7d0 | 911 | read_acpi_int(key->poll_hotkey.poll_handle, |
4be44fcd LB |
912 | key->poll_hotkey.poll_method, |
913 | key->poll_hotkey.poll_result); | |
914 | else if (method_type == ACTION_METHOD) | |
79cda7d0 | 915 | write_acpi_int(key->poll_hotkey.action_handle, |
4be44fcd LB |
916 | key->poll_hotkey.action_method, |
917 | value, NULL); | |
79cda7d0 LY |
918 | else |
919 | goto do_fail; | |
920 | ||
fb9802fa LY |
921 | } |
922 | } else { | |
cece9296 | 923 | printk(KERN_WARNING "Not supported\n"); |
d550d98d | 924 | return -EINVAL; |
fb9802fa | 925 | } |
d550d98d | 926 | return count; |
4be44fcd | 927 | do_fail: |
d550d98d | 928 | return -EINVAL; |
79cda7d0 | 929 | |
fb9802fa LY |
930 | } |
931 | ||
932 | static int __init hotkey_init(void) | |
933 | { | |
934 | int result; | |
935 | mode_t mode = S_IFREG | S_IRUGO | S_IWUGO; | |
936 | ||
fb9802fa LY |
937 | |
938 | if (acpi_disabled) | |
939 | return -ENODEV; | |
940 | ||
941 | if (acpi_specific_hotkey_enabled) { | |
942 | printk("Using specific hotkey driver\n"); | |
943 | return -ENODEV; | |
944 | } | |
945 | ||
946 | hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir); | |
947 | if (!hotkey_proc_dir) { | |
fb9802fa LY |
948 | return (-ENODEV); |
949 | } | |
950 | hotkey_proc_dir->owner = THIS_MODULE; | |
951 | ||
952 | hotkey_config = | |
953 | create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir); | |
954 | if (!hotkey_config) { | |
79cda7d0 | 955 | goto do_fail1; |
fb9802fa LY |
956 | } else { |
957 | hotkey_config->proc_fops = &hotkey_config_fops; | |
958 | hotkey_config->data = &global_hotkey_list; | |
959 | hotkey_config->owner = THIS_MODULE; | |
960 | hotkey_config->uid = 0; | |
961 | hotkey_config->gid = 0; | |
962 | } | |
963 | ||
964 | hotkey_poll_config = | |
965 | create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir); | |
966 | if (!hotkey_poll_config) { | |
79cda7d0 | 967 | goto do_fail2; |
fb9802fa LY |
968 | } else { |
969 | hotkey_poll_config->proc_fops = &hotkey_poll_config_fops; | |
970 | hotkey_poll_config->data = &global_hotkey_list; | |
971 | hotkey_poll_config->owner = THIS_MODULE; | |
972 | hotkey_poll_config->uid = 0; | |
973 | hotkey_poll_config->gid = 0; | |
974 | } | |
975 | ||
976 | hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir); | |
977 | if (!hotkey_action) { | |
79cda7d0 | 978 | goto do_fail3; |
fb9802fa LY |
979 | } else { |
980 | hotkey_action->proc_fops = &hotkey_action_fops; | |
981 | hotkey_action->owner = THIS_MODULE; | |
982 | hotkey_action->uid = 0; | |
983 | hotkey_action->gid = 0; | |
984 | } | |
985 | ||
986 | hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir); | |
987 | if (!hotkey_info) { | |
79cda7d0 | 988 | goto do_fail4; |
fb9802fa LY |
989 | } else { |
990 | hotkey_info->proc_fops = &hotkey_info_fops; | |
991 | hotkey_info->owner = THIS_MODULE; | |
992 | hotkey_info->uid = 0; | |
993 | hotkey_info->gid = 0; | |
994 | } | |
995 | ||
996 | result = acpi_bus_register_driver(&hotkey_driver); | |
79cda7d0 LY |
997 | if (result < 0) |
998 | goto do_fail5; | |
fb9802fa LY |
999 | global_hotkey_list.count = 0; |
1000 | global_hotkey_list.entries = &hotkey_entries; | |
1001 | ||
1002 | INIT_LIST_HEAD(&hotkey_entries); | |
1003 | ||
1004 | return (0); | |
79cda7d0 | 1005 | |
4be44fcd | 1006 | do_fail5: |
79cda7d0 | 1007 | remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); |
4be44fcd | 1008 | do_fail4: |
79cda7d0 | 1009 | remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); |
4be44fcd | 1010 | do_fail3: |
79cda7d0 | 1011 | remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); |
4be44fcd | 1012 | do_fail2: |
79cda7d0 | 1013 | remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); |
4be44fcd | 1014 | do_fail1: |
79cda7d0 LY |
1015 | remove_proc_entry(HOTKEY_PROC, acpi_root_dir); |
1016 | return (-ENODEV); | |
fb9802fa LY |
1017 | } |
1018 | ||
1019 | static void __exit hotkey_exit(void) | |
1020 | { | |
1021 | struct list_head *entries, *next; | |
1022 | ||
fb9802fa LY |
1023 | |
1024 | list_for_each_safe(entries, next, global_hotkey_list.entries) { | |
1025 | union acpi_hotkey *key = | |
1026 | container_of(entries, union acpi_hotkey, entries); | |
1027 | ||
1028 | acpi_os_wait_events_complete(NULL); | |
1029 | list_del(&key->link.entries); | |
1030 | global_hotkey_list.count--; | |
1031 | free_hotkey_device(key); | |
1032 | } | |
1033 | acpi_bus_unregister_driver(&hotkey_driver); | |
1034 | remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); | |
1035 | remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); | |
1036 | remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); | |
1037 | remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); | |
1038 | remove_proc_entry(HOTKEY_PROC, acpi_root_dir); | |
1039 | return; | |
1040 | } | |
1041 | ||
1042 | module_init(hotkey_init); | |
1043 | module_exit(hotkey_exit); |