diff options
author | Hiroshi Miura <miura@da-cha.org> | 2008-10-22 12:56:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-10-22 12:56:28 -0400 |
commit | c2bf092e02dcd51f46c6415b9df8606ed2d1e6f7 (patch) | |
tree | 51e394186a133693cd78e4646dd05e9e5dcd551a /drivers/staging | |
parent | 2515ddc6db8eb49a79f0fe5e67ff09ac7c81eab4 (diff) |
Staging: add pcc-acpi driver
Adds the pcc-acpi driver to the staging tree.
From: Hiroshi Miura <miura@da-cha.org>
Cc: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging')
-rw-r--r-- | drivers/staging/Kconfig | 2 | ||||
-rw-r--r-- | drivers/staging/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/pcc-acpi/Kconfig | 11 | ||||
-rw-r--r-- | drivers/staging/pcc-acpi/Makefile | 1 | ||||
-rw-r--r-- | drivers/staging/pcc-acpi/TODO | 7 | ||||
-rw-r--r-- | drivers/staging/pcc-acpi/pcc-acpi.c | 974 |
6 files changed, 996 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 2a79decd7dfc..65a86b704576 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig | |||
@@ -43,4 +43,6 @@ source "drivers/staging/echo/Kconfig" | |||
43 | 43 | ||
44 | source "drivers/staging/at76_usb/Kconfig" | 44 | source "drivers/staging/at76_usb/Kconfig" |
45 | 45 | ||
46 | source "drivers/staging/pcc-acpi/Kconfig" | ||
47 | |||
46 | endif # STAGING | 48 | endif # STAGING |
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 325bca4f71c0..e5c4a386c425 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile | |||
@@ -13,3 +13,4 @@ obj-$(CONFIG_W35UND) += winbond/ | |||
13 | obj-$(CONFIG_PRISM2_USB) += wlan-ng/ | 13 | obj-$(CONFIG_PRISM2_USB) += wlan-ng/ |
14 | obj-$(CONFIG_ECHO) += echo/ | 14 | obj-$(CONFIG_ECHO) += echo/ |
15 | obj-$(CONFIG_USB_ATMEL) += at76_usb/ | 15 | obj-$(CONFIG_USB_ATMEL) += at76_usb/ |
16 | obj-$(CONFIG_PCC_ACPI) += pcc-acpi/ | ||
diff --git a/drivers/staging/pcc-acpi/Kconfig b/drivers/staging/pcc-acpi/Kconfig new file mode 100644 index 000000000000..6720d4086baf --- /dev/null +++ b/drivers/staging/pcc-acpi/Kconfig | |||
@@ -0,0 +1,11 @@ | |||
1 | config PCC_ACPI | ||
2 | tristate "Panasonic ACPI Hotkey support" | ||
3 | depends on ACPI | ||
4 | default n | ||
5 | ---help--- | ||
6 | This driver provides support for Panasonic hotkeys through the | ||
7 | ACPI interface. This works for the Panasonic R1 (N variant), | ||
8 | R2, R3, T2, W2, and Y2 laptops. | ||
9 | |||
10 | To compile this driver as a module, choose M here. The module | ||
11 | will be called pcc-acpi. | ||
diff --git a/drivers/staging/pcc-acpi/Makefile b/drivers/staging/pcc-acpi/Makefile new file mode 100644 index 000000000000..f93b29edf61e --- /dev/null +++ b/drivers/staging/pcc-acpi/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-$(CONFIG_PCC_ACPI) += pcc-acpi.o | |||
diff --git a/drivers/staging/pcc-acpi/TODO b/drivers/staging/pcc-acpi/TODO new file mode 100644 index 000000000000..fab240982286 --- /dev/null +++ b/drivers/staging/pcc-acpi/TODO | |||
@@ -0,0 +1,7 @@ | |||
1 | TODO: | ||
2 | - Lindent fixes | ||
3 | - checkpatch.pl fixes | ||
4 | - verify that the acpi interface is correct | ||
5 | - remove /proc dependancy if needed (not sure yet.) | ||
6 | |||
7 | Please send any patches for this driver to Greg Kroah-Hartman <greg@kroah.com> | ||
diff --git a/drivers/staging/pcc-acpi/pcc-acpi.c b/drivers/staging/pcc-acpi/pcc-acpi.c new file mode 100644 index 000000000000..ffda32a10303 --- /dev/null +++ b/drivers/staging/pcc-acpi/pcc-acpi.c | |||
@@ -0,0 +1,974 @@ | |||
1 | /* | ||
2 | * Panasonic HotKey and lcd brightness control Extra driver | ||
3 | * (C) 2004 Hiroshi Miura <miura@da-cha.org> | ||
4 | * (C) 2004 NTT DATA Intellilink Co. http://www.intellilink.co.jp/ | ||
5 | * (C) YOKOTA Hiroshi <yokota (at) netlab. is. tsukuba. ac. jp> | ||
6 | * (C) 2004 David Bronaugh <dbronaugh> | ||
7 | * | ||
8 | * derived from toshiba_acpi.c, Copyright (C) 2002-2004 John Belmonte | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * publicshed by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | *--------------------------------------------------------------------------- | ||
24 | * | ||
25 | * ChangeLog: | ||
26 | * | ||
27 | * Nov.04, 2006 Hiroshi Miura <miura@da-cha.org> | ||
28 | * -v0.9 remove warning about section reference. | ||
29 | * remove acpi_os_free | ||
30 | * add /proc/acpi/pcc/brightness interface to allow HAL to access. | ||
31 | * merge dbronaugh's enhancement | ||
32 | * Aug.17, 2004 David Bronaugh (dbronaugh) | ||
33 | * - Added screen brightness setting interface | ||
34 | * Thanks to the FreeBSD crew (acpi_panasonic.c authors) | ||
35 | * for the ideas I needed to accomplish it | ||
36 | * | ||
37 | * May.29, 2006 Hiroshi Miura <miura@da-cha.org> | ||
38 | * -v0.8.4 follow to change keyinput structure | ||
39 | * thanks Fabian Yamaguchi <fabs@cs.tu-berlin.de>, | ||
40 | * Jacob Bower <jacob.bower@ic.ac.uk> and | ||
41 | * Hiroshi Yokota for providing solutions. | ||
42 | * | ||
43 | * Oct.02, 2004 Hiroshi Miura <miura@da-cha.org> | ||
44 | * -v0.8.2 merge code of YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>. | ||
45 | * Add sticky key mode interface. | ||
46 | * Refactoring acpi_pcc_generete_keyinput(). | ||
47 | * | ||
48 | * Sep.15, 2004 Hiroshi Miura <miura@da-cha.org> | ||
49 | * -v0.8 Generate key input event on input subsystem. | ||
50 | * This is based on yet another driver written by Ryuta Nakanishi. | ||
51 | * | ||
52 | * Sep.10, 2004 Hiroshi Miura <miura@da-cha.org> | ||
53 | * -v0.7 Change proc interface functions using seq_file | ||
54 | * facility as same as other ACPI drivers. | ||
55 | * | ||
56 | * Aug.28, 2004 Hiroshi Miura <miura@da-cha.org> | ||
57 | * -v0.6.4 Fix a silly error with status checking | ||
58 | * | ||
59 | * Aug.25, 2004 Hiroshi Miura <miura@da-cha.org> | ||
60 | * -v0.6.3 replace read_acpi_int by standard function acpi_evaluate_integer | ||
61 | * some clean up and make smart copyright notice. | ||
62 | * fix return value of pcc_acpi_get_key() | ||
63 | * fix checking return value of acpi_bus_register_driver() | ||
64 | * | ||
65 | * Aug.22, 2004 David Bronaugh <dbronaugh@linuxboxen.org> | ||
66 | * -v0.6.2 Add check on ACPI data (num_sifr) | ||
67 | * Coding style cleanups, better error messages/handling | ||
68 | * Fixed an off-by-one error in memory allocation | ||
69 | * | ||
70 | * Aug.21, 2004 David Bronaugh <dbronaugh@linuxboxen.org> | ||
71 | * -v0.6.1 Fix a silly error with status checking | ||
72 | * | ||
73 | * Aug.20, 2004 David Bronaugh <dbronaugh@linuxboxen.org> | ||
74 | * - v0.6 Correct brightness controls to reflect reality | ||
75 | * based on information gleaned by Hiroshi Miura | ||
76 | * and discussions with Hiroshi Miura | ||
77 | * | ||
78 | * Aug.10, 2004 Hiroshi Miura <miura@da-cha.org> | ||
79 | * - v0.5 support LCD brightness control | ||
80 | * based on the disclosed information by MEI. | ||
81 | * | ||
82 | * Jul.25, 2004 Hiroshi Miura <miura@da-cha.org> | ||
83 | * - v0.4 first post version | ||
84 | * add function to retrive SIFR | ||
85 | * | ||
86 | * Jul.24, 2004 Hiroshi Miura <miura@da-cha.org> | ||
87 | * - v0.3 get proper status of hotkey | ||
88 | * | ||
89 | * Jul.22, 2004 Hiroshi Miura <miura@da-cha.org> | ||
90 | * - v0.2 add HotKey handler | ||
91 | * | ||
92 | * Jul.17, 2004 Hiroshi Miura <miura@da-cha.org> | ||
93 | * - v0.1 start from toshiba_acpi driver written by John Belmonte | ||
94 | * | ||
95 | */ | ||
96 | |||
97 | #define ACPI_PCC_VERSION "0.9" | ||
98 | |||
99 | #include <linux/version.h> | ||
100 | #include <linux/kernel.h> | ||
101 | #include <linux/module.h> | ||
102 | #include <linux/init.h> | ||
103 | #include <linux/types.h> | ||
104 | #include <linux/proc_fs.h> | ||
105 | #include <linux/ctype.h> | ||
106 | #include <linux/seq_file.h> | ||
107 | #include <asm/uaccess.h> | ||
108 | #include <acpi/acpi_bus.h> | ||
109 | #include <acpi/acpi_drivers.h> | ||
110 | #include <linux/input.h> | ||
111 | |||
112 | |||
113 | /************************************************************************* | ||
114 | * "seq" file template definition. | ||
115 | */ | ||
116 | /* "seq" initializer */ | ||
117 | #define SEQ_OPEN_FS(_open_func_name_, _show_func_name_) \ | ||
118 | static int _open_func_name_(struct inode *inode, struct file *file) \ | ||
119 | { \ | ||
120 | return single_open(file, _show_func_name_, PDE(inode)->data); \ | ||
121 | } | ||
122 | |||
123 | /*------------------------------------------------------------------------- | ||
124 | * "seq" fops template for read-only files. | ||
125 | */ | ||
126 | #define SEQ_FILEOPS_R(_open_func_name_) \ | ||
127 | { \ | ||
128 | .open = _open_func_name_, \ | ||
129 | .read = seq_read, \ | ||
130 | .llseek = seq_lseek, \ | ||
131 | .release = single_release, \ | ||
132 | } | ||
133 | |||
134 | /*------------------------------------------------------------------------ | ||
135 | * "seq" fops template for read-write files. | ||
136 | */ | ||
137 | #define SEQ_FILEOPS_RW(_open_func_name_, _write_func_name_) \ | ||
138 | { \ | ||
139 | .open = _open_func_name_ , \ | ||
140 | .read = seq_read, \ | ||
141 | .write = _write_func_name_, \ | ||
142 | .llseek = seq_lseek, \ | ||
143 | .release = single_release, \ | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * "seq" file template definition ended. | ||
148 | *************************************************************************** | ||
149 | */ | ||
150 | #ifndef ACPI_HOTKEY_COMPONENT | ||
151 | #define ACPI_HOTKEY_COMPONENT 0x10000000 | ||
152 | #endif | ||
153 | |||
154 | #define _COMPONENT ACPI_HOTKEY_COMPONENT | ||
155 | ACPI_MODULE_NAME("pcc_acpi") | ||
156 | |||
157 | MODULE_AUTHOR("Hiroshi Miura"); | ||
158 | MODULE_DESCRIPTION("ACPI HotKey driver for Panasonic Lets Note laptops"); | ||
159 | MODULE_LICENSE("GPL"); | ||
160 | |||
161 | #define LOGPREFIX "pcc_acpi: " | ||
162 | |||
163 | /**************************************************** | ||
164 | * Define ACPI PATHs | ||
165 | ****************************************************/ | ||
166 | /* Lets note hotkeys */ | ||
167 | #define METHOD_HKEY_QUERY "HINF" | ||
168 | #define METHOD_HKEY_SQTY "SQTY" | ||
169 | #define METHOD_HKEY_SINF "SINF" | ||
170 | #define METHOD_HKEY_SSET "SSET" | ||
171 | #define HKEY_NOTIFY 0x80 | ||
172 | |||
173 | /* for brightness control */ | ||
174 | #define LCD_MAX_BRIGHTNESS 255 | ||
175 | /* This may be magical -- beware */ | ||
176 | #define LCD_BRIGHTNESS_INCREMENT 17 | ||
177 | /* Registers of SINF */ | ||
178 | #define SINF_LCD_BRIGHTNESS 4 | ||
179 | |||
180 | /******************************************************************* | ||
181 | * | ||
182 | * definitions for /proc/ interface | ||
183 | * | ||
184 | *******************************************************************/ | ||
185 | #define ACPI_PCC_DRIVER_NAME "PCC Extra Driver" | ||
186 | #define ACPI_PCC_DEVICE_NAME "PCCExtra" | ||
187 | #define ACPI_PCC_CLASS "pcc" | ||
188 | #define PROC_PCC ACPI_PCC_CLASS | ||
189 | |||
190 | #define ACPI_PCC_INPUT_PHYS "panasonic/hkey0" | ||
191 | |||
192 | /* This is transitional definition */ | ||
193 | #ifndef KEY_BATT | ||
194 | # define KEY_BATT 227 | ||
195 | #endif | ||
196 | |||
197 | #define PROC_STR_MAX_LEN 8 | ||
198 | |||
199 | /* LCD_TYPEs: 0 = Normal, 1 = Semi-transparent | ||
200 | ENV_STATEs: Normal temp=0x01, High temp=0x81, N/A=0x00 | ||
201 | */ | ||
202 | enum SINF_BITS { SINF_NUM_BATTERIES = 0, | ||
203 | SINF_LCD_TYPE, /* 1 */ | ||
204 | SINF_AC_MAX_BRIGHT, SINF_AC_MIN_BRIGHT, SINF_AC_CUR_BRIGHT, /* 2, 3, 4 */ | ||
205 | /* 4 = R1 only handle SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT and don't know AC state */ | ||
206 | SINF_DC_MAX_BRIGHT, SINF_DC_MIN_BRIGHT, SINF_DC_CUR_BRIGHT, /* 5, 6, 7 */ | ||
207 | SINF_MUTE, | ||
208 | SINF_RESERVED, SINF_ENV_STATE, /* 9, 10 */ | ||
209 | SINF_STICKY_KEY = 0x80, | ||
210 | }; | ||
211 | |||
212 | static int acpi_pcc_hotkey_add(struct acpi_device *device); | ||
213 | static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type); | ||
214 | static int acpi_pcc_hotkey_resume(struct acpi_device *device); | ||
215 | |||
216 | static const struct acpi_device_id pcc_device_ids[] = { | ||
217 | {"MAT0012", 0}, | ||
218 | {"MAT0013", 0}, | ||
219 | {"MAT0018", 0}, | ||
220 | {"MAT0019", 0}, | ||
221 | {"", 0}, | ||
222 | }; | ||
223 | MODULE_DEVICE_TABLE(acpi, pcc_device_ids); | ||
224 | |||
225 | static struct acpi_driver acpi_pcc_driver = { | ||
226 | .name = ACPI_PCC_DRIVER_NAME, | ||
227 | .class = ACPI_PCC_CLASS, | ||
228 | .ids = pcc_device_ids, | ||
229 | .ops = { | ||
230 | .add = acpi_pcc_hotkey_add, | ||
231 | .remove = acpi_pcc_hotkey_remove, | ||
232 | .resume = acpi_pcc_hotkey_resume, | ||
233 | }, | ||
234 | }; | ||
235 | |||
236 | struct acpi_hotkey { | ||
237 | acpi_handle handle; | ||
238 | struct acpi_device *device; | ||
239 | struct proc_dir_entry *proc_dir_entry; | ||
240 | unsigned long num_sifr; | ||
241 | unsigned long status; | ||
242 | struct input_dev *input_dev; | ||
243 | int sticky_mode; | ||
244 | }; | ||
245 | |||
246 | struct pcc_keyinput { | ||
247 | struct acpi_hotkey *hotkey; | ||
248 | int key_mode; | ||
249 | }; | ||
250 | |||
251 | /* -------------------------------------------------------------------------- | ||
252 | method access functions | ||
253 | -------------------------------------------------------------------------- */ | ||
254 | static int acpi_pcc_write_sset(struct acpi_hotkey *hotkey, int func, int val) | ||
255 | { | ||
256 | union acpi_object in_objs[] = { | ||
257 | { .integer.type = ACPI_TYPE_INTEGER, | ||
258 | .integer.value = func, }, | ||
259 | { .integer.type = ACPI_TYPE_INTEGER, | ||
260 | .integer.value = val, }, | ||
261 | }; | ||
262 | struct acpi_object_list params = { | ||
263 | .count = ARRAY_SIZE(in_objs), | ||
264 | .pointer = in_objs, | ||
265 | }; | ||
266 | acpi_status status = AE_OK; | ||
267 | |||
268 | ACPI_FUNCTION_TRACE("acpi_pcc_write_sset"); | ||
269 | |||
270 | status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SSET, ¶ms, NULL); | ||
271 | |||
272 | return_VALUE(status == AE_OK); | ||
273 | } | ||
274 | |||
275 | static inline int acpi_pcc_get_sqty(struct acpi_device *device) | ||
276 | { | ||
277 | unsigned long s; | ||
278 | acpi_status status; | ||
279 | |||
280 | ACPI_FUNCTION_TRACE("acpi_pcc_get_sqty"); | ||
281 | |||
282 | status = acpi_evaluate_integer(device->handle, METHOD_HKEY_SQTY, NULL, &s); | ||
283 | if (ACPI_SUCCESS(status)) { | ||
284 | return_VALUE(s); | ||
285 | } else { | ||
286 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
287 | "evaluation error HKEY.SQTY\n")); | ||
288 | return_VALUE(-EINVAL); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static int acpi_pcc_retrieve_biosdata(struct acpi_hotkey *hotkey, u32* sinf) | ||
293 | { | ||
294 | acpi_status status; | ||
295 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
296 | union acpi_object *hkey = NULL; | ||
297 | int i; | ||
298 | |||
299 | ACPI_FUNCTION_TRACE("acpi_pcc_retrieve_biosdata"); | ||
300 | |||
301 | status = acpi_evaluate_object(hotkey->handle, METHOD_HKEY_SINF, 0 , &buffer); | ||
302 | if (ACPI_FAILURE(status)) { | ||
303 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "evaluation error HKEY.SINF\n")); | ||
304 | return_VALUE(0); | ||
305 | } | ||
306 | |||
307 | hkey = buffer.pointer; | ||
308 | if (!hkey || (hkey->type != ACPI_TYPE_PACKAGE)) { | ||
309 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF\n")); | ||
310 | goto end; | ||
311 | } | ||
312 | |||
313 | if (hotkey->num_sifr < hkey->package.count) { | ||
314 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "SQTY reports bad SINF length\n")); | ||
315 | status = AE_ERROR; | ||
316 | goto end; | ||
317 | } | ||
318 | |||
319 | for (i = 0; i < hkey->package.count; i++) { | ||
320 | union acpi_object *element = &(hkey->package.elements[i]); | ||
321 | if (likely(element->type == ACPI_TYPE_INTEGER)) { | ||
322 | sinf[i] = element->integer.value; | ||
323 | } else { | ||
324 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid HKEY.SINF data\n")); | ||
325 | } | ||
326 | } | ||
327 | sinf[hkey->package.count] = -1; | ||
328 | |||
329 | end: | ||
330 | kfree(buffer.pointer); | ||
331 | return_VALUE(status == AE_OK); | ||
332 | } | ||
333 | |||
334 | static int acpi_pcc_read_sinf_field(struct seq_file *seq, int field) | ||
335 | { | ||
336 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private; | ||
337 | u32* sinf = kmalloc(sizeof(u32) * (hotkey->num_sifr + 1), GFP_KERNEL); | ||
338 | |||
339 | ACPI_FUNCTION_TRACE("acpi_pcc_read_sinf_field"); | ||
340 | |||
341 | if (!sinf) { | ||
342 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate %li bytes\n", | ||
343 | sizeof(u32) * hotkey->num_sifr)); | ||
344 | return_VALUE(0); | ||
345 | } | ||
346 | |||
347 | if (acpi_pcc_retrieve_biosdata(hotkey, sinf)) { | ||
348 | seq_printf(seq, "%u\n", sinf[field]); | ||
349 | } else { | ||
350 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); | ||
351 | } | ||
352 | |||
353 | kfree(sinf); | ||
354 | return_VALUE(0); | ||
355 | } | ||
356 | |||
357 | /* -------------------------------------------------------------------------- | ||
358 | user interface functions | ||
359 | -------------------------------------------------------------------------- */ | ||
360 | /* read methods */ | ||
361 | /* Sinf read methods */ | ||
362 | #define PCC_SINF_READ_F(_name_, FUNC) \ | ||
363 | static int _name_ (struct seq_file *seq, void *offset) \ | ||
364 | { \ | ||
365 | return acpi_pcc_read_sinf_field(seq, (FUNC)); \ | ||
366 | } | ||
367 | |||
368 | PCC_SINF_READ_F(acpi_pcc_numbatteries_show, SINF_NUM_BATTERIES); | ||
369 | PCC_SINF_READ_F(acpi_pcc_lcdtype_show, SINF_LCD_TYPE); | ||
370 | PCC_SINF_READ_F(acpi_pcc_ac_brightness_max_show, SINF_AC_MAX_BRIGHT); | ||
371 | PCC_SINF_READ_F(acpi_pcc_ac_brightness_min_show, SINF_AC_MIN_BRIGHT); | ||
372 | PCC_SINF_READ_F(acpi_pcc_ac_brightness_show, SINF_AC_CUR_BRIGHT); | ||
373 | PCC_SINF_READ_F(acpi_pcc_dc_brightness_max_show, SINF_DC_MAX_BRIGHT); | ||
374 | PCC_SINF_READ_F(acpi_pcc_dc_brightness_min_show, SINF_DC_MIN_BRIGHT); | ||
375 | PCC_SINF_READ_F(acpi_pcc_dc_brightness_show, SINF_DC_CUR_BRIGHT); | ||
376 | PCC_SINF_READ_F(acpi_pcc_brightness_show, SINF_AC_CUR_BRIGHT); | ||
377 | PCC_SINF_READ_F(acpi_pcc_mute_show, SINF_MUTE); | ||
378 | |||
379 | static int acpi_pcc_sticky_key_show(struct seq_file *seq, void *offset) | ||
380 | { | ||
381 | struct acpi_hotkey *hotkey = seq->private; | ||
382 | |||
383 | ACPI_FUNCTION_TRACE("acpi_pcc_sticky_key_show"); | ||
384 | |||
385 | if (!hotkey || !hotkey->device) { | ||
386 | return_VALUE(0); | ||
387 | } | ||
388 | |||
389 | seq_printf(seq, "%d\n", hotkey->sticky_mode); | ||
390 | |||
391 | return_VALUE(0); | ||
392 | } | ||
393 | |||
394 | static int acpi_pcc_keyinput_show(struct seq_file *seq, void *offset) | ||
395 | { | ||
396 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private; | ||
397 | struct input_dev *hotk_input_dev = hotkey->input_dev; | ||
398 | struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev); | ||
399 | |||
400 | ACPI_FUNCTION_TRACE("acpi_pcc_keyinput_show"); | ||
401 | |||
402 | seq_printf(seq, "%d\n", keyinput->key_mode); | ||
403 | |||
404 | return_VALUE(0); | ||
405 | } | ||
406 | |||
407 | static int acpi_pcc_version_show(struct seq_file *seq, void *offset) | ||
408 | { | ||
409 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *) seq->private; | ||
410 | |||
411 | ACPI_FUNCTION_TRACE("acpi_pcc_version_show"); | ||
412 | |||
413 | if (!hotkey || !hotkey->device) | ||
414 | return 0; | ||
415 | |||
416 | seq_printf(seq, "%s version %s\n", ACPI_PCC_DRIVER_NAME, ACPI_PCC_VERSION); | ||
417 | seq_printf(seq, "%li functions\n", hotkey->num_sifr); | ||
418 | |||
419 | return_VALUE(0); | ||
420 | } | ||
421 | |||
422 | /* write methods */ | ||
423 | static ssize_t acpi_pcc_write_single_flag (struct file *file, | ||
424 | const char __user *buffer, | ||
425 | size_t count, | ||
426 | int sinf_func) | ||
427 | { | ||
428 | struct seq_file *seq = file->private_data; | ||
429 | struct acpi_hotkey *hotkey = seq->private; | ||
430 | char write_string[PROC_STR_MAX_LEN]; | ||
431 | u32 val; | ||
432 | |||
433 | ACPI_FUNCTION_TRACE("acpi_pcc_write_single_flag"); | ||
434 | |||
435 | if (!hotkey || (count > sizeof(write_string) - 1)) { | ||
436 | return_VALUE(-EINVAL); | ||
437 | } | ||
438 | |||
439 | if (copy_from_user(write_string, buffer, count)) { | ||
440 | return_VALUE(-EFAULT); | ||
441 | } | ||
442 | write_string[count] = '\0'; | ||
443 | |||
444 | if (sscanf(write_string, "%i", &val) == 1 && (val == 0 || val == 1)) { | ||
445 | acpi_pcc_write_sset(hotkey, sinf_func, val); | ||
446 | } | ||
447 | |||
448 | return_VALUE(count); | ||
449 | } | ||
450 | |||
451 | static unsigned long acpi_pcc_write_brightness(struct file *file, const char __user *buffer, | ||
452 | size_t count, | ||
453 | int min_index, int max_index, | ||
454 | int cur_index) | ||
455 | { | ||
456 | struct seq_file *seq = (struct seq_file *)file->private_data; | ||
457 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private; | ||
458 | char write_string[PROC_STR_MAX_LEN]; | ||
459 | u32 bright; | ||
460 | u32* sinf = kmalloc(sizeof(u32) * (hotkey->num_sifr + 1), GFP_KERNEL); | ||
461 | |||
462 | ACPI_FUNCTION_TRACE("acpi_pcc_write_brightness"); | ||
463 | |||
464 | if (!hotkey || (count > sizeof(write_string) - 1)) | ||
465 | return_VALUE(-EINVAL); | ||
466 | |||
467 | if (!sinf) { | ||
468 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate %li bytes\n", | ||
469 | sizeof(u32) * hotkey->num_sifr)); | ||
470 | return_VALUE(-EFAULT); | ||
471 | } | ||
472 | |||
473 | if (copy_from_user(write_string, buffer, count)) | ||
474 | return_VALUE(-EFAULT); | ||
475 | |||
476 | write_string[count] = '\0'; | ||
477 | |||
478 | if (!acpi_pcc_retrieve_biosdata(hotkey, sinf)) { | ||
479 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't retrieve BIOS data\n")); | ||
480 | goto end; | ||
481 | } | ||
482 | |||
483 | if (sscanf(write_string, "%i", &bright) == 1 && | ||
484 | bright >= sinf[min_index] && bright <= sinf[max_index]) { | ||
485 | acpi_pcc_write_sset(hotkey, cur_index, bright); | ||
486 | } | ||
487 | |||
488 | end: | ||
489 | kfree(sinf); | ||
490 | return_VALUE(count); | ||
491 | } | ||
492 | |||
493 | static ssize_t acpi_pcc_write_ac_brightness(struct file *file, const char __user *buffer, | ||
494 | size_t count, loff_t *ppos) | ||
495 | { | ||
496 | return acpi_pcc_write_brightness(file, buffer, count, SINF_AC_MIN_BRIGHT, | ||
497 | SINF_AC_MAX_BRIGHT, | ||
498 | SINF_AC_CUR_BRIGHT); | ||
499 | } | ||
500 | |||
501 | static ssize_t acpi_pcc_write_dc_brightness(struct file *file, const char __user *buffer, | ||
502 | size_t count, loff_t *ppos) | ||
503 | { | ||
504 | return acpi_pcc_write_brightness(file, buffer, count, SINF_DC_MIN_BRIGHT, | ||
505 | SINF_DC_MAX_BRIGHT, | ||
506 | SINF_DC_CUR_BRIGHT); | ||
507 | } | ||
508 | |||
509 | static ssize_t acpi_pcc_write_no_brightness(struct file *file, const char __user *buffer, | ||
510 | size_t count, loff_t *ppos) | ||
511 | { | ||
512 | return acpi_pcc_write_brightness(file, buffer, count, SINF_AC_MIN_BRIGHT, | ||
513 | SINF_AC_MAX_BRIGHT, | ||
514 | SINF_AC_CUR_BRIGHT); | ||
515 | } | ||
516 | |||
517 | static ssize_t acpi_pcc_write_mute (struct file *file, | ||
518 | const char __user *buffer, | ||
519 | size_t count, loff_t *ppos) | ||
520 | { | ||
521 | return acpi_pcc_write_single_flag(file, buffer, count, SINF_MUTE); | ||
522 | } | ||
523 | |||
524 | static ssize_t acpi_pcc_write_sticky_key (struct file *file, | ||
525 | const char __user *buffer, | ||
526 | size_t count, loff_t *ppos) | ||
527 | { | ||
528 | return acpi_pcc_write_single_flag(file, buffer, count, SINF_STICKY_KEY); | ||
529 | } | ||
530 | |||
531 | static ssize_t acpi_pcc_write_keyinput(struct file *file, const char __user *buffer, | ||
532 | size_t count, loff_t *ppos) | ||
533 | { | ||
534 | struct seq_file *seq = (struct seq_file *)file->private_data; | ||
535 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *)seq->private; | ||
536 | struct pcc_keyinput *keyinput; | ||
537 | char write_string[PROC_STR_MAX_LEN]; | ||
538 | int key_mode; | ||
539 | |||
540 | ACPI_FUNCTION_TRACE("acpi_pcc_write_keyinput"); | ||
541 | |||
542 | if (!hotkey || (count > sizeof(write_string) - 1)) | ||
543 | return_VALUE(-EINVAL); | ||
544 | |||
545 | if (copy_from_user(write_string, buffer, count)) | ||
546 | return_VALUE(-EFAULT); | ||
547 | |||
548 | write_string[count] = '\0'; | ||
549 | |||
550 | if (sscanf(write_string, "%i", &key_mode) == 1 && (key_mode == 0 || key_mode == 1)) { | ||
551 | keyinput = (struct pcc_keyinput *)input_get_drvdata(hotkey->input_dev); | ||
552 | keyinput->key_mode = key_mode; | ||
553 | } | ||
554 | |||
555 | return_VALUE(count); | ||
556 | } | ||
557 | |||
558 | /* -------------------------------------------------------------------------- | ||
559 | hotkey driver | ||
560 | -------------------------------------------------------------------------- */ | ||
561 | static void acpi_pcc_generete_keyinput(struct acpi_hotkey *hotkey) | ||
562 | { | ||
563 | struct input_dev *hotk_input_dev = hotkey->input_dev; | ||
564 | struct pcc_keyinput *keyinput = input_get_drvdata(hotk_input_dev); | ||
565 | int hinf = hotkey->status; | ||
566 | int key_code, hkey_num; | ||
567 | const int key_map[] = { | ||
568 | /* 0 */ -1, | ||
569 | /* 1 */ KEY_BRIGHTNESSDOWN, | ||
570 | /* 2 */ KEY_BRIGHTNESSUP, | ||
571 | /* 3 */ -1, /* vga/lcd switch event is not occur on hotkey driver. */ | ||
572 | /* 4 */ KEY_MUTE, | ||
573 | /* 5 */ KEY_VOLUMEDOWN, | ||
574 | /* 6 */ KEY_VOLUMEUP, | ||
575 | /* 7 */ KEY_SLEEP, | ||
576 | /* 8 */ -1, /* Change CPU boost: do nothing */ | ||
577 | /* 9 */ KEY_BATT, | ||
578 | /* 10 */ KEY_SUSPEND, | ||
579 | }; | ||
580 | |||
581 | ACPI_FUNCTION_TRACE("acpi_pcc_generete_keyinput"); | ||
582 | |||
583 | if (keyinput->key_mode == 0) { return_VOID; } | ||
584 | |||
585 | hkey_num = hinf & 0xf; | ||
586 | |||
587 | if (( 0 > hkey_num ) || | ||
588 | (hkey_num > ARRAY_SIZE(key_map))) { | ||
589 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
590 | "hotkey number out of range: %d\n", | ||
591 | hkey_num)); | ||
592 | return_VOID; | ||
593 | } | ||
594 | |||
595 | key_code = key_map[hkey_num]; | ||
596 | |||
597 | if (key_code != -1) { | ||
598 | int pushed = (hinf & 0x80) ? TRUE : FALSE; | ||
599 | |||
600 | input_report_key(hotk_input_dev, key_code, pushed); | ||
601 | input_sync(hotk_input_dev); | ||
602 | } | ||
603 | } | ||
604 | |||
605 | static int acpi_pcc_hotkey_get_key(struct acpi_hotkey *hotkey) | ||
606 | { | ||
607 | unsigned long result; | ||
608 | acpi_status status = AE_OK; | ||
609 | |||
610 | ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_get_key"); | ||
611 | |||
612 | status = acpi_evaluate_integer(hotkey->handle, METHOD_HKEY_QUERY, NULL, &result); | ||
613 | if (likely(ACPI_SUCCESS(status))) { | ||
614 | hotkey->status = result; | ||
615 | } else { | ||
616 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "error getting hotkey status\n")); | ||
617 | } | ||
618 | |||
619 | return_VALUE(status == AE_OK); | ||
620 | } | ||
621 | |||
622 | void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data) | ||
623 | { | ||
624 | struct acpi_hotkey *hotkey = (struct acpi_hotkey *) data; | ||
625 | |||
626 | ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_notify"); | ||
627 | |||
628 | switch(event) { | ||
629 | case HKEY_NOTIFY: | ||
630 | if (acpi_pcc_hotkey_get_key(hotkey)) { | ||
631 | /* generate event like '"pcc HKEY 00000080 00000084"' when Fn+F4 pressed */ | ||
632 | acpi_bus_generate_proc_event(hotkey->device, event, hotkey->status); | ||
633 | } | ||
634 | acpi_pcc_generete_keyinput(hotkey); | ||
635 | break; | ||
636 | default: | ||
637 | /* nothing to do */ | ||
638 | break; | ||
639 | } | ||
640 | return_VOID; | ||
641 | } | ||
642 | |||
643 | /* -------------------------------------------------------------------------- | ||
644 | FS Interface (/proc) | ||
645 | -------------------------------------------------------------------------- */ | ||
646 | /* oepn proc file fs*/ | ||
647 | SEQ_OPEN_FS(acpi_pcc_dc_brightness_open_fs, acpi_pcc_dc_brightness_show); | ||
648 | SEQ_OPEN_FS(acpi_pcc_numbatteries_open_fs, acpi_pcc_numbatteries_show); | ||
649 | SEQ_OPEN_FS(acpi_pcc_lcdtype_open_fs, acpi_pcc_lcdtype_show); | ||
650 | SEQ_OPEN_FS(acpi_pcc_ac_brightness_max_open_fs, acpi_pcc_ac_brightness_max_show); | ||
651 | SEQ_OPEN_FS(acpi_pcc_ac_brightness_min_open_fs, acpi_pcc_ac_brightness_min_show); | ||
652 | SEQ_OPEN_FS(acpi_pcc_ac_brightness_open_fs, acpi_pcc_ac_brightness_show); | ||
653 | SEQ_OPEN_FS(acpi_pcc_dc_brightness_max_open_fs, acpi_pcc_dc_brightness_max_show); | ||
654 | SEQ_OPEN_FS(acpi_pcc_dc_brightness_min_open_fs, acpi_pcc_dc_brightness_min_show); | ||
655 | SEQ_OPEN_FS(acpi_pcc_brightness_open_fs, acpi_pcc_brightness_show); | ||
656 | SEQ_OPEN_FS(acpi_pcc_mute_open_fs, acpi_pcc_mute_show); | ||
657 | SEQ_OPEN_FS(acpi_pcc_version_open_fs, acpi_pcc_version_show); | ||
658 | SEQ_OPEN_FS(acpi_pcc_keyinput_open_fs, acpi_pcc_keyinput_show); | ||
659 | SEQ_OPEN_FS(acpi_pcc_sticky_key_open_fs, acpi_pcc_sticky_key_show); | ||
660 | |||
661 | typedef struct file_operations fops_t; | ||
662 | static fops_t acpi_pcc_numbatteries_fops = SEQ_FILEOPS_R (acpi_pcc_numbatteries_open_fs); | ||
663 | static fops_t acpi_pcc_lcdtype_fops = SEQ_FILEOPS_R (acpi_pcc_lcdtype_open_fs); | ||
664 | static fops_t acpi_pcc_mute_fops = SEQ_FILEOPS_RW(acpi_pcc_mute_open_fs, acpi_pcc_write_mute); | ||
665 | static fops_t acpi_pcc_ac_brightness_fops = SEQ_FILEOPS_RW(acpi_pcc_ac_brightness_open_fs, acpi_pcc_write_ac_brightness); | ||
666 | static fops_t acpi_pcc_ac_brightness_max_fops = SEQ_FILEOPS_R(acpi_pcc_ac_brightness_max_open_fs); | ||
667 | static fops_t acpi_pcc_ac_brightness_min_fops = SEQ_FILEOPS_R(acpi_pcc_ac_brightness_min_open_fs); | ||
668 | static fops_t acpi_pcc_dc_brightness_fops = SEQ_FILEOPS_RW(acpi_pcc_dc_brightness_open_fs, acpi_pcc_write_dc_brightness); | ||
669 | static fops_t acpi_pcc_dc_brightness_max_fops = SEQ_FILEOPS_R(acpi_pcc_dc_brightness_max_open_fs); | ||
670 | static fops_t acpi_pcc_dc_brightness_min_fops = SEQ_FILEOPS_R(acpi_pcc_dc_brightness_min_open_fs); | ||
671 | static fops_t acpi_pcc_brightness_fops = SEQ_FILEOPS_RW(acpi_pcc_brightness_open_fs, acpi_pcc_write_no_brightness); | ||
672 | static fops_t acpi_pcc_sticky_key_fops = SEQ_FILEOPS_RW(acpi_pcc_sticky_key_open_fs, acpi_pcc_write_sticky_key); | ||
673 | static fops_t acpi_pcc_keyinput_fops = SEQ_FILEOPS_RW(acpi_pcc_keyinput_open_fs, acpi_pcc_write_keyinput); | ||
674 | static fops_t acpi_pcc_version_fops = SEQ_FILEOPS_R (acpi_pcc_version_open_fs); | ||
675 | |||
676 | typedef struct _ProcItem | ||
677 | { | ||
678 | const char* name; | ||
679 | struct file_operations *fops; | ||
680 | mode_t flag; | ||
681 | } ProcItem; | ||
682 | |||
683 | /* Note: These functions map *exactly* to the SINF/SSET functions */ | ||
684 | ProcItem pcc_proc_items_sifr[] = | ||
685 | { | ||
686 | { "num_batteries", &acpi_pcc_numbatteries_fops, S_IRUGO }, | ||
687 | { "lcd_type", &acpi_pcc_lcdtype_fops, S_IRUGO }, | ||
688 | { "ac_brightness_max" , &acpi_pcc_ac_brightness_max_fops,S_IRUGO }, | ||
689 | { "ac_brightness_min" , &acpi_pcc_ac_brightness_min_fops,S_IRUGO }, | ||
690 | { "ac_brightness" , &acpi_pcc_ac_brightness_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
691 | { "dc_brightness_max" , &acpi_pcc_dc_brightness_max_fops,S_IRUGO }, | ||
692 | { "dc_brightness_min" , &acpi_pcc_dc_brightness_min_fops,S_IRUGO }, | ||
693 | { "dc_brightness" , &acpi_pcc_dc_brightness_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
694 | { "brightness" , &acpi_pcc_brightness_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
695 | { "mute", &acpi_pcc_mute_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
696 | { NULL, NULL, 0 }, | ||
697 | }; | ||
698 | |||
699 | ProcItem pcc_proc_items[] = | ||
700 | { | ||
701 | { "sticky_key", &acpi_pcc_sticky_key_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
702 | { "keyinput", &acpi_pcc_keyinput_fops, S_IFREG | S_IRUGO | S_IWUSR }, | ||
703 | { "version", &acpi_pcc_version_fops, S_IRUGO }, | ||
704 | { NULL, NULL, 0 }, | ||
705 | }; | ||
706 | |||
707 | static int acpi_pcc_add_device(struct acpi_device *device, | ||
708 | ProcItem *proc_items, | ||
709 | int num) | ||
710 | { | ||
711 | struct proc_dir_entry* proc; | ||
712 | ProcItem* item; | ||
713 | int i; | ||
714 | struct acpi_hotkey *hotkey = (struct acpi_hotkey*)acpi_driver_data(device); | ||
715 | |||
716 | for (item = proc_items, i = 0; item->name && i < num; ++item, ++i) { | ||
717 | proc = create_proc_entry(item->name, item->flag, hotkey->proc_dir_entry); | ||
718 | if (likely(proc)) { | ||
719 | proc->proc_fops = item->fops; | ||
720 | proc->data = hotkey; | ||
721 | proc->owner = THIS_MODULE; | ||
722 | } else { | ||
723 | while (i-- > 0) { | ||
724 | item--; | ||
725 | remove_proc_entry(item->name, hotkey->proc_dir_entry); | ||
726 | } | ||
727 | return -ENODEV; | ||
728 | } | ||
729 | } | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static int acpi_pcc_proc_init(struct acpi_device *device) | ||
734 | { | ||
735 | struct proc_dir_entry* acpi_pcc_dir; | ||
736 | struct acpi_hotkey *hotkey = (struct acpi_hotkey*)acpi_driver_data(device); | ||
737 | acpi_status status; | ||
738 | |||
739 | ACPI_FUNCTION_TRACE("acpi_pcc_proc_init"); | ||
740 | |||
741 | acpi_pcc_dir = proc_mkdir(PROC_PCC, acpi_root_dir); | ||
742 | |||
743 | if (unlikely(!acpi_pcc_dir)) { | ||
744 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't create dir in /proc\n")); | ||
745 | return_VALUE(-ENODEV); | ||
746 | } | ||
747 | |||
748 | acpi_pcc_dir->owner = THIS_MODULE; | ||
749 | hotkey->proc_dir_entry = acpi_pcc_dir; | ||
750 | |||
751 | status = acpi_pcc_add_device(device, pcc_proc_items_sifr, hotkey->num_sifr); | ||
752 | status |= acpi_pcc_add_device(device, pcc_proc_items, sizeof(pcc_proc_items)/sizeof(ProcItem)); | ||
753 | if (unlikely(status)) { | ||
754 | remove_proc_entry(PROC_PCC, acpi_root_dir); | ||
755 | hotkey->proc_dir_entry = NULL; | ||
756 | return_VALUE(-ENODEV); | ||
757 | } | ||
758 | |||
759 | return_VALUE(status); | ||
760 | } | ||
761 | |||
762 | static void acpi_pcc_remove_device(struct acpi_device *device, | ||
763 | ProcItem *proc_items, | ||
764 | int num) | ||
765 | { | ||
766 | struct acpi_hotkey *hotkey = (struct acpi_hotkey*)acpi_driver_data(device); | ||
767 | ProcItem* item; | ||
768 | int i; | ||
769 | |||
770 | for (item = proc_items, i = 0; item->name != NULL && i < num; ++item, ++i) { | ||
771 | remove_proc_entry(item->name, hotkey->proc_dir_entry); | ||
772 | } | ||
773 | |||
774 | return; | ||
775 | } | ||
776 | |||
777 | /* -------------------------------------------------------------------------- | ||
778 | input init | ||
779 | -------------------------------------------------------------------------- */ | ||
780 | static int acpi_pcc_init_input(struct acpi_hotkey *hotkey) | ||
781 | { | ||
782 | struct input_dev *hotk_input_dev; | ||
783 | struct pcc_keyinput *pcc_keyinput; | ||
784 | |||
785 | ACPI_FUNCTION_TRACE("acpi_pcc_init_input"); | ||
786 | |||
787 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) | ||
788 | hotk_input_dev = input_allocate_device(); | ||
789 | if (!hotk_input_dev) { | ||
790 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate input device for hotkey")); | ||
791 | return_VALUE(-ENOMEM); | ||
792 | } | ||
793 | #else | ||
794 | hotk_input_dev = kcalloc(1, sizeof(struct input_dev),GFP_KERNEL); | ||
795 | if (hotk_input_dev == NULL) { | ||
796 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate mem for hotkey")); | ||
797 | return_VALUE(-ENOMEM); | ||
798 | } | ||
799 | #endif | ||
800 | |||
801 | pcc_keyinput = kcalloc(1,sizeof(struct pcc_keyinput),GFP_KERNEL); | ||
802 | |||
803 | if (pcc_keyinput == NULL) { | ||
804 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate mem for hotkey")); | ||
805 | input_unregister_device(hotk_input_dev); | ||
806 | return_VALUE(-ENOMEM); | ||
807 | } | ||
808 | |||
809 | hotk_input_dev->evbit[0] = BIT(EV_KEY); | ||
810 | |||
811 | set_bit(KEY_BRIGHTNESSDOWN, hotk_input_dev->keybit); | ||
812 | set_bit(KEY_BRIGHTNESSUP, hotk_input_dev->keybit); | ||
813 | set_bit(KEY_MUTE, hotk_input_dev->keybit); | ||
814 | set_bit(KEY_VOLUMEDOWN, hotk_input_dev->keybit); | ||
815 | set_bit(KEY_VOLUMEUP, hotk_input_dev->keybit); | ||
816 | set_bit(KEY_SLEEP, hotk_input_dev->keybit); | ||
817 | set_bit(KEY_BATT, hotk_input_dev->keybit); | ||
818 | set_bit(KEY_SUSPEND, hotk_input_dev->keybit); | ||
819 | |||
820 | hotk_input_dev->name = ACPI_PCC_DRIVER_NAME; | ||
821 | hotk_input_dev->phys = ACPI_PCC_INPUT_PHYS; | ||
822 | hotk_input_dev->id.bustype = 0x1a; /* XXX FIXME: BUS_I8042? */ | ||
823 | hotk_input_dev->id.vendor = 0x0001; | ||
824 | hotk_input_dev->id.product = 0x0001; | ||
825 | hotk_input_dev->id.version = 0x0100; | ||
826 | |||
827 | pcc_keyinput->key_mode = 1; /* default on */ | ||
828 | pcc_keyinput->hotkey = hotkey; | ||
829 | |||
830 | input_set_drvdata(hotk_input_dev, pcc_keyinput); | ||
831 | |||
832 | hotkey->input_dev = hotk_input_dev; | ||
833 | |||
834 | |||
835 | input_register_device(hotk_input_dev); | ||
836 | |||
837 | return_VALUE(0); | ||
838 | } | ||
839 | |||
840 | /* -------------------------------------------------------------------------- | ||
841 | module init | ||
842 | -------------------------------------------------------------------------- */ | ||
843 | |||
844 | static int acpi_pcc_hotkey_resume(struct acpi_device *device) | ||
845 | { | ||
846 | struct acpi_hotkey *hotkey = acpi_driver_data(device); | ||
847 | acpi_status status = AE_OK; | ||
848 | |||
849 | ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_resume"); | ||
850 | |||
851 | if (device == NULL || hotkey == NULL) { return_VALUE(-EINVAL); } | ||
852 | |||
853 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", hotkey->sticky_mode)); | ||
854 | |||
855 | status = acpi_pcc_write_sset(hotkey, SINF_STICKY_KEY, hotkey->sticky_mode); | ||
856 | |||
857 | return_VALUE(status == AE_OK ? 0 : -EINVAL); | ||
858 | } | ||
859 | |||
860 | static int acpi_pcc_hotkey_add (struct acpi_device *device) | ||
861 | { | ||
862 | acpi_status status = AE_OK; | ||
863 | struct acpi_hotkey *hotkey = NULL; | ||
864 | int num_sifr, result; | ||
865 | |||
866 | ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_add"); | ||
867 | |||
868 | if (!device) { | ||
869 | return_VALUE(-EINVAL); | ||
870 | } | ||
871 | |||
872 | num_sifr = acpi_pcc_get_sqty(device); | ||
873 | |||
874 | if (num_sifr > 255) { | ||
875 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large")); | ||
876 | return_VALUE(-ENODEV); | ||
877 | } | ||
878 | |||
879 | hotkey = kmalloc(sizeof(struct acpi_hotkey), GFP_KERNEL); | ||
880 | if (!hotkey) { | ||
881 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Couldn't allocate mem for hotkey")); | ||
882 | return_VALUE(-ENOMEM); | ||
883 | } | ||
884 | |||
885 | memset(hotkey, 0, sizeof(struct acpi_hotkey)); | ||
886 | |||
887 | hotkey->device = device; | ||
888 | hotkey->handle = device->handle; | ||
889 | hotkey->num_sifr = num_sifr; | ||
890 | acpi_driver_data(device) = hotkey; | ||
891 | strcpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); | ||
892 | strcpy(acpi_device_class(device), ACPI_PCC_CLASS); | ||
893 | |||
894 | status = acpi_install_notify_handler ( | ||
895 | hotkey->handle, | ||
896 | ACPI_DEVICE_NOTIFY, | ||
897 | acpi_pcc_hotkey_notify, | ||
898 | hotkey); | ||
899 | |||
900 | if (ACPI_FAILURE(status)) { | ||
901 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing notify handler\n")); | ||
902 | kfree(hotkey); | ||
903 | return_VALUE(-ENODEV); | ||
904 | } | ||
905 | |||
906 | result = acpi_pcc_init_input(hotkey); | ||
907 | if (result) { | ||
908 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error installing keyinput handler\n")); | ||
909 | kfree(hotkey); | ||
910 | return_VALUE(result); | ||
911 | } | ||
912 | |||
913 | return_VALUE(acpi_pcc_proc_init(device)); | ||
914 | } | ||
915 | |||
916 | static int __init acpi_pcc_init(void) | ||
917 | { | ||
918 | int result = 0; | ||
919 | |||
920 | ACPI_FUNCTION_TRACE("acpi_pcc_init"); | ||
921 | |||
922 | if (acpi_disabled) { | ||
923 | return_VALUE(-ENODEV); | ||
924 | } | ||
925 | |||
926 | result = acpi_bus_register_driver(&acpi_pcc_driver); | ||
927 | if (result < 0) { | ||
928 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error registering hotkey driver\n")); | ||
929 | return_VALUE(-ENODEV); | ||
930 | } | ||
931 | |||
932 | return_VALUE(0); | ||
933 | } | ||
934 | |||
935 | module_init(acpi_pcc_init); | ||
936 | |||
937 | static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) | ||
938 | { | ||
939 | acpi_status status = AE_OK; | ||
940 | struct acpi_hotkey *hotkey = acpi_driver_data(device); | ||
941 | |||
942 | ACPI_FUNCTION_TRACE("acpi_pcc_hotkey_remove"); | ||
943 | |||
944 | if (!device || !hotkey) | ||
945 | return_VALUE(-EINVAL); | ||
946 | |||
947 | if (hotkey->proc_dir_entry) { | ||
948 | acpi_pcc_remove_device(device, pcc_proc_items_sifr, hotkey->num_sifr); | ||
949 | acpi_pcc_remove_device(device, pcc_proc_items, sizeof(pcc_proc_items)/sizeof(ProcItem)); | ||
950 | remove_proc_entry(PROC_PCC, acpi_root_dir); | ||
951 | } | ||
952 | |||
953 | status = acpi_remove_notify_handler(hotkey->handle, | ||
954 | ACPI_DEVICE_NOTIFY, acpi_pcc_hotkey_notify); | ||
955 | |||
956 | if (ACPI_FAILURE(status)) | ||
957 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error removing notify handler\n")); | ||
958 | |||
959 | input_unregister_device(hotkey->input_dev); | ||
960 | |||
961 | kfree(hotkey); | ||
962 | return_VALUE(status == AE_OK); | ||
963 | } | ||
964 | |||
965 | static void __exit acpi_pcc_exit(void) | ||
966 | { | ||
967 | ACPI_FUNCTION_TRACE("acpi_pcc_exit"); | ||
968 | |||
969 | acpi_bus_unregister_driver(&acpi_pcc_driver); | ||
970 | |||
971 | return_VOID; | ||
972 | } | ||
973 | |||
974 | module_exit(acpi_pcc_exit); | ||