diff options
Diffstat (limited to 'drivers/misc/fujitsu-laptop.c')
-rw-r--r-- | drivers/misc/fujitsu-laptop.c | 1293 |
1 files changed, 0 insertions, 1293 deletions
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c deleted file mode 100644 index 65dc41540c62..000000000000 --- a/drivers/misc/fujitsu-laptop.c +++ /dev/null | |||
@@ -1,1293 +0,0 @@ | |||
1 | /*-*-linux-c-*-*/ | ||
2 | |||
3 | /* | ||
4 | Copyright (C) 2007,2008 Jonathan Woithe <jwoithe@physics.adelaide.edu.au> | ||
5 | Copyright (C) 2008 Peter Gruber <nokos@gmx.net> | ||
6 | Copyright (C) 2008 Tony Vroon <tony@linx.net> | ||
7 | Based on earlier work: | ||
8 | Copyright (C) 2003 Shane Spencer <shane@bogomip.com> | ||
9 | Adrian Yee <brewt-fujitsu@brewt.org> | ||
10 | |||
11 | Templated from msi-laptop.c and thinkpad_acpi.c which is copyright | ||
12 | by its respective authors. | ||
13 | |||
14 | This program is free software; you can redistribute it and/or modify | ||
15 | it under the terms of the GNU General Public License as published by | ||
16 | the Free Software Foundation; either version 2 of the License, or | ||
17 | (at your option) any later version. | ||
18 | |||
19 | This program is distributed in the hope that it will be useful, but | ||
20 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
22 | General Public License for more details. | ||
23 | |||
24 | You should have received a copy of the GNU General Public License | ||
25 | along with this program; if not, write to the Free Software | ||
26 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
27 | 02110-1301, USA. | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * fujitsu-laptop.c - Fujitsu laptop support, providing access to additional | ||
32 | * features made available on a range of Fujitsu laptops including the | ||
33 | * P2xxx/P5xxx/S6xxx/S7xxx series. | ||
34 | * | ||
35 | * This driver exports a few files in /sys/devices/platform/fujitsu-laptop/; | ||
36 | * others may be added at a later date. | ||
37 | * | ||
38 | * lcd_level - Screen brightness: contains a single integer in the | ||
39 | * range 0..7. (rw) | ||
40 | * | ||
41 | * In addition to these platform device attributes the driver | ||
42 | * registers itself in the Linux backlight control subsystem and is | ||
43 | * available to userspace under /sys/class/backlight/fujitsu-laptop/. | ||
44 | * | ||
45 | * Hotkeys present on certain Fujitsu laptops (eg: the S6xxx series) are | ||
46 | * also supported by this driver. | ||
47 | * | ||
48 | * This driver has been tested on a Fujitsu Lifebook S6410, S7020 and | ||
49 | * P8010. It should work on most P-series and S-series Lifebooks, but | ||
50 | * YMMV. | ||
51 | * | ||
52 | * The module parameter use_alt_lcd_levels switches between different ACPI | ||
53 | * brightness controls which are used by different Fujitsu laptops. In most | ||
54 | * cases the correct method is automatically detected. "use_alt_lcd_levels=1" | ||
55 | * is applicable for a Fujitsu Lifebook S6410 if autodetection fails. | ||
56 | * | ||
57 | */ | ||
58 | |||
59 | #include <linux/module.h> | ||
60 | #include <linux/kernel.h> | ||
61 | #include <linux/init.h> | ||
62 | #include <linux/acpi.h> | ||
63 | #include <linux/dmi.h> | ||
64 | #include <linux/backlight.h> | ||
65 | #include <linux/input.h> | ||
66 | #include <linux/kfifo.h> | ||
67 | #include <linux/video_output.h> | ||
68 | #include <linux/platform_device.h> | ||
69 | #ifdef CONFIG_LEDS_CLASS | ||
70 | #include <linux/leds.h> | ||
71 | #endif | ||
72 | |||
73 | #define FUJITSU_DRIVER_VERSION "0.5.0" | ||
74 | |||
75 | #define FUJITSU_LCD_N_LEVELS 8 | ||
76 | |||
77 | #define ACPI_FUJITSU_CLASS "fujitsu" | ||
78 | #define ACPI_FUJITSU_HID "FUJ02B1" | ||
79 | #define ACPI_FUJITSU_DRIVER_NAME "Fujitsu laptop FUJ02B1 ACPI brightness driver" | ||
80 | #define ACPI_FUJITSU_DEVICE_NAME "Fujitsu FUJ02B1" | ||
81 | #define ACPI_FUJITSU_HOTKEY_HID "FUJ02E3" | ||
82 | #define ACPI_FUJITSU_HOTKEY_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" | ||
83 | #define ACPI_FUJITSU_HOTKEY_DEVICE_NAME "Fujitsu FUJ02E3" | ||
84 | |||
85 | #define ACPI_FUJITSU_NOTIFY_CODE1 0x80 | ||
86 | |||
87 | #define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 | ||
88 | #define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 | ||
89 | |||
90 | /* FUNC interface - command values */ | ||
91 | #define FUNC_RFKILL 0x1000 | ||
92 | #define FUNC_LEDS 0x1001 | ||
93 | #define FUNC_BUTTONS 0x1002 | ||
94 | #define FUNC_BACKLIGHT 0x1004 | ||
95 | |||
96 | /* FUNC interface - responses */ | ||
97 | #define UNSUPPORTED_CMD 0x80000000 | ||
98 | |||
99 | #ifdef CONFIG_LEDS_CLASS | ||
100 | /* FUNC interface - LED control */ | ||
101 | #define FUNC_LED_OFF 0x1 | ||
102 | #define FUNC_LED_ON 0x30001 | ||
103 | #define KEYBOARD_LAMPS 0x100 | ||
104 | #define LOGOLAMP_POWERON 0x2000 | ||
105 | #define LOGOLAMP_ALWAYS 0x4000 | ||
106 | #endif | ||
107 | |||
108 | /* Hotkey details */ | ||
109 | #define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ | ||
110 | #define KEY2_CODE 0x411 | ||
111 | #define KEY3_CODE 0x412 | ||
112 | #define KEY4_CODE 0x413 | ||
113 | |||
114 | #define MAX_HOTKEY_RINGBUFFER_SIZE 100 | ||
115 | #define RINGBUFFERSIZE 40 | ||
116 | |||
117 | /* Debugging */ | ||
118 | #define FUJLAPTOP_LOG ACPI_FUJITSU_HID ": " | ||
119 | #define FUJLAPTOP_ERR KERN_ERR FUJLAPTOP_LOG | ||
120 | #define FUJLAPTOP_NOTICE KERN_NOTICE FUJLAPTOP_LOG | ||
121 | #define FUJLAPTOP_INFO KERN_INFO FUJLAPTOP_LOG | ||
122 | #define FUJLAPTOP_DEBUG KERN_DEBUG FUJLAPTOP_LOG | ||
123 | |||
124 | #define FUJLAPTOP_DBG_ALL 0xffff | ||
125 | #define FUJLAPTOP_DBG_ERROR 0x0001 | ||
126 | #define FUJLAPTOP_DBG_WARN 0x0002 | ||
127 | #define FUJLAPTOP_DBG_INFO 0x0004 | ||
128 | #define FUJLAPTOP_DBG_TRACE 0x0008 | ||
129 | |||
130 | #define dbg_printk(a_dbg_level, format, arg...) \ | ||
131 | do { if (dbg_level & a_dbg_level) \ | ||
132 | printk(FUJLAPTOP_DEBUG "%s: " format, __func__ , ## arg); \ | ||
133 | } while (0) | ||
134 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
135 | #define vdbg_printk(a_dbg_level, format, arg...) \ | ||
136 | dbg_printk(a_dbg_level, format, ## arg) | ||
137 | #else | ||
138 | #define vdbg_printk(a_dbg_level, format, arg...) | ||
139 | #endif | ||
140 | |||
141 | /* Device controlling the backlight and associated keys */ | ||
142 | struct fujitsu_t { | ||
143 | acpi_handle acpi_handle; | ||
144 | struct acpi_device *dev; | ||
145 | struct input_dev *input; | ||
146 | char phys[32]; | ||
147 | struct backlight_device *bl_device; | ||
148 | struct platform_device *pf_device; | ||
149 | int keycode1, keycode2, keycode3, keycode4; | ||
150 | |||
151 | unsigned int max_brightness; | ||
152 | unsigned int brightness_changed; | ||
153 | unsigned int brightness_level; | ||
154 | }; | ||
155 | |||
156 | static struct fujitsu_t *fujitsu; | ||
157 | static int use_alt_lcd_levels = -1; | ||
158 | static int disable_brightness_adjust = -1; | ||
159 | |||
160 | /* Device used to access other hotkeys on the laptop */ | ||
161 | struct fujitsu_hotkey_t { | ||
162 | acpi_handle acpi_handle; | ||
163 | struct acpi_device *dev; | ||
164 | struct input_dev *input; | ||
165 | char phys[32]; | ||
166 | struct platform_device *pf_device; | ||
167 | struct kfifo *fifo; | ||
168 | spinlock_t fifo_lock; | ||
169 | int rfkill_state; | ||
170 | int logolamp_registered; | ||
171 | int kblamps_registered; | ||
172 | }; | ||
173 | |||
174 | static struct fujitsu_hotkey_t *fujitsu_hotkey; | ||
175 | |||
176 | static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, | ||
177 | void *data); | ||
178 | |||
179 | #ifdef CONFIG_LEDS_CLASS | ||
180 | static enum led_brightness logolamp_get(struct led_classdev *cdev); | ||
181 | static void logolamp_set(struct led_classdev *cdev, | ||
182 | enum led_brightness brightness); | ||
183 | |||
184 | struct led_classdev logolamp_led = { | ||
185 | .name = "fujitsu::logolamp", | ||
186 | .brightness_get = logolamp_get, | ||
187 | .brightness_set = logolamp_set | ||
188 | }; | ||
189 | |||
190 | static enum led_brightness kblamps_get(struct led_classdev *cdev); | ||
191 | static void kblamps_set(struct led_classdev *cdev, | ||
192 | enum led_brightness brightness); | ||
193 | |||
194 | struct led_classdev kblamps_led = { | ||
195 | .name = "fujitsu::kblamps", | ||
196 | .brightness_get = kblamps_get, | ||
197 | .brightness_set = kblamps_set | ||
198 | }; | ||
199 | #endif | ||
200 | |||
201 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
202 | static u32 dbg_level = 0x03; | ||
203 | #endif | ||
204 | |||
205 | static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data); | ||
206 | |||
207 | /* Fujitsu ACPI interface function */ | ||
208 | |||
209 | static int call_fext_func(int cmd, int arg0, int arg1, int arg2) | ||
210 | { | ||
211 | acpi_status status = AE_OK; | ||
212 | union acpi_object params[4] = { | ||
213 | { .type = ACPI_TYPE_INTEGER }, | ||
214 | { .type = ACPI_TYPE_INTEGER }, | ||
215 | { .type = ACPI_TYPE_INTEGER }, | ||
216 | { .type = ACPI_TYPE_INTEGER } | ||
217 | }; | ||
218 | struct acpi_object_list arg_list = { 4, ¶ms[0] }; | ||
219 | struct acpi_buffer output; | ||
220 | union acpi_object out_obj; | ||
221 | acpi_handle handle = NULL; | ||
222 | |||
223 | status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); | ||
224 | if (ACPI_FAILURE(status)) { | ||
225 | vdbg_printk(FUJLAPTOP_DBG_ERROR, | ||
226 | "FUNC interface is not present\n"); | ||
227 | return -ENODEV; | ||
228 | } | ||
229 | |||
230 | params[0].integer.value = cmd; | ||
231 | params[1].integer.value = arg0; | ||
232 | params[2].integer.value = arg1; | ||
233 | params[3].integer.value = arg2; | ||
234 | |||
235 | output.length = sizeof(out_obj); | ||
236 | output.pointer = &out_obj; | ||
237 | |||
238 | status = acpi_evaluate_object(handle, NULL, &arg_list, &output); | ||
239 | if (ACPI_FAILURE(status)) { | ||
240 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
241 | "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", | ||
242 | cmd, arg0, arg1, arg2); | ||
243 | return -ENODEV; | ||
244 | } | ||
245 | |||
246 | if (out_obj.type != ACPI_TYPE_INTEGER) { | ||
247 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
248 | "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " | ||
249 | "return an integer\n", | ||
250 | cmd, arg0, arg1, arg2); | ||
251 | return -ENODEV; | ||
252 | } | ||
253 | |||
254 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
255 | "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", | ||
256 | cmd, arg0, arg1, arg2, (int)out_obj.integer.value); | ||
257 | return out_obj.integer.value; | ||
258 | } | ||
259 | |||
260 | #ifdef CONFIG_LEDS_CLASS | ||
261 | /* LED class callbacks */ | ||
262 | |||
263 | static void logolamp_set(struct led_classdev *cdev, | ||
264 | enum led_brightness brightness) | ||
265 | { | ||
266 | if (brightness >= LED_FULL) { | ||
267 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); | ||
268 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_ON); | ||
269 | } else if (brightness >= LED_HALF) { | ||
270 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_ON); | ||
271 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, FUNC_LED_OFF); | ||
272 | } else { | ||
273 | call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, FUNC_LED_OFF); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | static void kblamps_set(struct led_classdev *cdev, | ||
278 | enum led_brightness brightness) | ||
279 | { | ||
280 | if (brightness >= LED_FULL) | ||
281 | call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); | ||
282 | else | ||
283 | call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); | ||
284 | } | ||
285 | |||
286 | static enum led_brightness logolamp_get(struct led_classdev *cdev) | ||
287 | { | ||
288 | enum led_brightness brightness = LED_OFF; | ||
289 | int poweron, always; | ||
290 | |||
291 | poweron = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); | ||
292 | if (poweron == FUNC_LED_ON) { | ||
293 | brightness = LED_HALF; | ||
294 | always = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); | ||
295 | if (always == FUNC_LED_ON) | ||
296 | brightness = LED_FULL; | ||
297 | } | ||
298 | return brightness; | ||
299 | } | ||
300 | |||
301 | static enum led_brightness kblamps_get(struct led_classdev *cdev) | ||
302 | { | ||
303 | enum led_brightness brightness = LED_OFF; | ||
304 | |||
305 | if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) | ||
306 | brightness = LED_FULL; | ||
307 | |||
308 | return brightness; | ||
309 | } | ||
310 | #endif | ||
311 | |||
312 | /* Hardware access for LCD brightness control */ | ||
313 | |||
314 | static int set_lcd_level(int level) | ||
315 | { | ||
316 | acpi_status status = AE_OK; | ||
317 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
318 | struct acpi_object_list arg_list = { 1, &arg0 }; | ||
319 | acpi_handle handle = NULL; | ||
320 | |||
321 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", | ||
322 | level); | ||
323 | |||
324 | if (level < 0 || level >= fujitsu->max_brightness) | ||
325 | return -EINVAL; | ||
326 | |||
327 | if (!fujitsu) | ||
328 | return -EINVAL; | ||
329 | |||
330 | status = acpi_get_handle(fujitsu->acpi_handle, "SBLL", &handle); | ||
331 | if (ACPI_FAILURE(status)) { | ||
332 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBLL not present\n"); | ||
333 | return -ENODEV; | ||
334 | } | ||
335 | |||
336 | arg0.integer.value = level; | ||
337 | |||
338 | status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); | ||
339 | if (ACPI_FAILURE(status)) | ||
340 | return -ENODEV; | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static int set_lcd_level_alt(int level) | ||
346 | { | ||
347 | acpi_status status = AE_OK; | ||
348 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
349 | struct acpi_object_list arg_list = { 1, &arg0 }; | ||
350 | acpi_handle handle = NULL; | ||
351 | |||
352 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", | ||
353 | level); | ||
354 | |||
355 | if (level < 0 || level >= fujitsu->max_brightness) | ||
356 | return -EINVAL; | ||
357 | |||
358 | if (!fujitsu) | ||
359 | return -EINVAL; | ||
360 | |||
361 | status = acpi_get_handle(fujitsu->acpi_handle, "SBL2", &handle); | ||
362 | if (ACPI_FAILURE(status)) { | ||
363 | vdbg_printk(FUJLAPTOP_DBG_ERROR, "SBL2 not present\n"); | ||
364 | return -ENODEV; | ||
365 | } | ||
366 | |||
367 | arg0.integer.value = level; | ||
368 | |||
369 | status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); | ||
370 | if (ACPI_FAILURE(status)) | ||
371 | return -ENODEV; | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static int get_lcd_level(void) | ||
377 | { | ||
378 | unsigned long long state = 0; | ||
379 | acpi_status status = AE_OK; | ||
380 | |||
381 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n"); | ||
382 | |||
383 | status = | ||
384 | acpi_evaluate_integer(fujitsu->acpi_handle, "GBLL", NULL, &state); | ||
385 | if (status < 0) | ||
386 | return status; | ||
387 | |||
388 | fujitsu->brightness_level = state & 0x0fffffff; | ||
389 | |||
390 | if (state & 0x80000000) | ||
391 | fujitsu->brightness_changed = 1; | ||
392 | else | ||
393 | fujitsu->brightness_changed = 0; | ||
394 | |||
395 | return fujitsu->brightness_level; | ||
396 | } | ||
397 | |||
398 | static int get_max_brightness(void) | ||
399 | { | ||
400 | unsigned long long state = 0; | ||
401 | acpi_status status = AE_OK; | ||
402 | |||
403 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n"); | ||
404 | |||
405 | status = | ||
406 | acpi_evaluate_integer(fujitsu->acpi_handle, "RBLL", NULL, &state); | ||
407 | if (status < 0) | ||
408 | return status; | ||
409 | |||
410 | fujitsu->max_brightness = state; | ||
411 | |||
412 | return fujitsu->max_brightness; | ||
413 | } | ||
414 | |||
415 | /* Backlight device stuff */ | ||
416 | |||
417 | static int bl_get_brightness(struct backlight_device *b) | ||
418 | { | ||
419 | return get_lcd_level(); | ||
420 | } | ||
421 | |||
422 | static int bl_update_status(struct backlight_device *b) | ||
423 | { | ||
424 | int ret; | ||
425 | if (b->props.power == 4) | ||
426 | ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3); | ||
427 | else | ||
428 | ret = call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0); | ||
429 | if (ret != 0) | ||
430 | vdbg_printk(FUJLAPTOP_DBG_ERROR, | ||
431 | "Unable to adjust backlight power, error code %i\n", | ||
432 | ret); | ||
433 | |||
434 | if (use_alt_lcd_levels) | ||
435 | ret = set_lcd_level_alt(b->props.brightness); | ||
436 | else | ||
437 | ret = set_lcd_level(b->props.brightness); | ||
438 | if (ret != 0) | ||
439 | vdbg_printk(FUJLAPTOP_DBG_ERROR, | ||
440 | "Unable to adjust LCD brightness, error code %i\n", | ||
441 | ret); | ||
442 | return ret; | ||
443 | } | ||
444 | |||
445 | static struct backlight_ops fujitsubl_ops = { | ||
446 | .get_brightness = bl_get_brightness, | ||
447 | .update_status = bl_update_status, | ||
448 | }; | ||
449 | |||
450 | /* Platform LCD brightness device */ | ||
451 | |||
452 | static ssize_t | ||
453 | show_max_brightness(struct device *dev, | ||
454 | struct device_attribute *attr, char *buf) | ||
455 | { | ||
456 | |||
457 | int ret; | ||
458 | |||
459 | ret = get_max_brightness(); | ||
460 | if (ret < 0) | ||
461 | return ret; | ||
462 | |||
463 | return sprintf(buf, "%i\n", ret); | ||
464 | } | ||
465 | |||
466 | static ssize_t | ||
467 | show_brightness_changed(struct device *dev, | ||
468 | struct device_attribute *attr, char *buf) | ||
469 | { | ||
470 | |||
471 | int ret; | ||
472 | |||
473 | ret = fujitsu->brightness_changed; | ||
474 | if (ret < 0) | ||
475 | return ret; | ||
476 | |||
477 | return sprintf(buf, "%i\n", ret); | ||
478 | } | ||
479 | |||
480 | static ssize_t show_lcd_level(struct device *dev, | ||
481 | struct device_attribute *attr, char *buf) | ||
482 | { | ||
483 | |||
484 | int ret; | ||
485 | |||
486 | ret = get_lcd_level(); | ||
487 | if (ret < 0) | ||
488 | return ret; | ||
489 | |||
490 | return sprintf(buf, "%i\n", ret); | ||
491 | } | ||
492 | |||
493 | static ssize_t store_lcd_level(struct device *dev, | ||
494 | struct device_attribute *attr, const char *buf, | ||
495 | size_t count) | ||
496 | { | ||
497 | |||
498 | int level, ret; | ||
499 | |||
500 | if (sscanf(buf, "%i", &level) != 1 | ||
501 | || (level < 0 || level >= fujitsu->max_brightness)) | ||
502 | return -EINVAL; | ||
503 | |||
504 | if (use_alt_lcd_levels) | ||
505 | ret = set_lcd_level_alt(level); | ||
506 | else | ||
507 | ret = set_lcd_level(level); | ||
508 | if (ret < 0) | ||
509 | return ret; | ||
510 | |||
511 | ret = get_lcd_level(); | ||
512 | if (ret < 0) | ||
513 | return ret; | ||
514 | |||
515 | return count; | ||
516 | } | ||
517 | |||
518 | static ssize_t | ||
519 | ignore_store(struct device *dev, | ||
520 | struct device_attribute *attr, const char *buf, size_t count) | ||
521 | { | ||
522 | return count; | ||
523 | } | ||
524 | |||
525 | static ssize_t | ||
526 | show_lid_state(struct device *dev, | ||
527 | struct device_attribute *attr, char *buf) | ||
528 | { | ||
529 | if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) | ||
530 | return sprintf(buf, "unknown\n"); | ||
531 | if (fujitsu_hotkey->rfkill_state & 0x100) | ||
532 | return sprintf(buf, "open\n"); | ||
533 | else | ||
534 | return sprintf(buf, "closed\n"); | ||
535 | } | ||
536 | |||
537 | static ssize_t | ||
538 | show_dock_state(struct device *dev, | ||
539 | struct device_attribute *attr, char *buf) | ||
540 | { | ||
541 | if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) | ||
542 | return sprintf(buf, "unknown\n"); | ||
543 | if (fujitsu_hotkey->rfkill_state & 0x200) | ||
544 | return sprintf(buf, "docked\n"); | ||
545 | else | ||
546 | return sprintf(buf, "undocked\n"); | ||
547 | } | ||
548 | |||
549 | static ssize_t | ||
550 | show_radios_state(struct device *dev, | ||
551 | struct device_attribute *attr, char *buf) | ||
552 | { | ||
553 | if (fujitsu_hotkey->rfkill_state == UNSUPPORTED_CMD) | ||
554 | return sprintf(buf, "unknown\n"); | ||
555 | if (fujitsu_hotkey->rfkill_state & 0x20) | ||
556 | return sprintf(buf, "on\n"); | ||
557 | else | ||
558 | return sprintf(buf, "killed\n"); | ||
559 | } | ||
560 | |||
561 | static DEVICE_ATTR(max_brightness, 0444, show_max_brightness, ignore_store); | ||
562 | static DEVICE_ATTR(brightness_changed, 0444, show_brightness_changed, | ||
563 | ignore_store); | ||
564 | static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level); | ||
565 | static DEVICE_ATTR(lid, 0444, show_lid_state, ignore_store); | ||
566 | static DEVICE_ATTR(dock, 0444, show_dock_state, ignore_store); | ||
567 | static DEVICE_ATTR(radios, 0444, show_radios_state, ignore_store); | ||
568 | |||
569 | static struct attribute *fujitsupf_attributes[] = { | ||
570 | &dev_attr_brightness_changed.attr, | ||
571 | &dev_attr_max_brightness.attr, | ||
572 | &dev_attr_lcd_level.attr, | ||
573 | &dev_attr_lid.attr, | ||
574 | &dev_attr_dock.attr, | ||
575 | &dev_attr_radios.attr, | ||
576 | NULL | ||
577 | }; | ||
578 | |||
579 | static struct attribute_group fujitsupf_attribute_group = { | ||
580 | .attrs = fujitsupf_attributes | ||
581 | }; | ||
582 | |||
583 | static struct platform_driver fujitsupf_driver = { | ||
584 | .driver = { | ||
585 | .name = "fujitsu-laptop", | ||
586 | .owner = THIS_MODULE, | ||
587 | } | ||
588 | }; | ||
589 | |||
590 | static void dmi_check_cb_common(const struct dmi_system_id *id) | ||
591 | { | ||
592 | acpi_handle handle; | ||
593 | printk(KERN_INFO "fujitsu-laptop: Identified laptop model '%s'.\n", | ||
594 | id->ident); | ||
595 | if (use_alt_lcd_levels == -1) { | ||
596 | if (ACPI_SUCCESS(acpi_get_handle(NULL, | ||
597 | "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) | ||
598 | use_alt_lcd_levels = 1; | ||
599 | else | ||
600 | use_alt_lcd_levels = 0; | ||
601 | vdbg_printk(FUJLAPTOP_DBG_TRACE, "auto-detected usealt as " | ||
602 | "%i\n", use_alt_lcd_levels); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | static int dmi_check_cb_s6410(const struct dmi_system_id *id) | ||
607 | { | ||
608 | dmi_check_cb_common(id); | ||
609 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | ||
610 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static int dmi_check_cb_s6420(const struct dmi_system_id *id) | ||
615 | { | ||
616 | dmi_check_cb_common(id); | ||
617 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | ||
618 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) | ||
623 | { | ||
624 | dmi_check_cb_common(id); | ||
625 | fujitsu->keycode1 = KEY_HELP; /* "Support" */ | ||
626 | fujitsu->keycode3 = KEY_SWITCHVIDEOMODE; /* "Presentation" */ | ||
627 | fujitsu->keycode4 = KEY_WWW; /* "Internet" */ | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static struct dmi_system_id fujitsu_dmi_table[] = { | ||
632 | { | ||
633 | .ident = "Fujitsu Siemens S6410", | ||
634 | .matches = { | ||
635 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
636 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6410"), | ||
637 | }, | ||
638 | .callback = dmi_check_cb_s6410}, | ||
639 | { | ||
640 | .ident = "Fujitsu Siemens S6420", | ||
641 | .matches = { | ||
642 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
643 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), | ||
644 | }, | ||
645 | .callback = dmi_check_cb_s6420}, | ||
646 | { | ||
647 | .ident = "Fujitsu LifeBook P8010", | ||
648 | .matches = { | ||
649 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | ||
650 | DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P8010"), | ||
651 | }, | ||
652 | .callback = dmi_check_cb_p8010}, | ||
653 | {} | ||
654 | }; | ||
655 | |||
656 | /* ACPI device for LCD brightness control */ | ||
657 | |||
658 | static int acpi_fujitsu_add(struct acpi_device *device) | ||
659 | { | ||
660 | acpi_status status; | ||
661 | acpi_handle handle; | ||
662 | int result = 0; | ||
663 | int state = 0; | ||
664 | struct input_dev *input; | ||
665 | int error; | ||
666 | |||
667 | if (!device) | ||
668 | return -EINVAL; | ||
669 | |||
670 | fujitsu->acpi_handle = device->handle; | ||
671 | sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_DEVICE_NAME); | ||
672 | sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); | ||
673 | device->driver_data = fujitsu; | ||
674 | |||
675 | status = acpi_install_notify_handler(device->handle, | ||
676 | ACPI_DEVICE_NOTIFY, | ||
677 | acpi_fujitsu_notify, fujitsu); | ||
678 | |||
679 | if (ACPI_FAILURE(status)) { | ||
680 | printk(KERN_ERR "Error installing notify handler\n"); | ||
681 | error = -ENODEV; | ||
682 | goto err_stop; | ||
683 | } | ||
684 | |||
685 | fujitsu->input = input = input_allocate_device(); | ||
686 | if (!input) { | ||
687 | error = -ENOMEM; | ||
688 | goto err_uninstall_notify; | ||
689 | } | ||
690 | |||
691 | snprintf(fujitsu->phys, sizeof(fujitsu->phys), | ||
692 | "%s/video/input0", acpi_device_hid(device)); | ||
693 | |||
694 | input->name = acpi_device_name(device); | ||
695 | input->phys = fujitsu->phys; | ||
696 | input->id.bustype = BUS_HOST; | ||
697 | input->id.product = 0x06; | ||
698 | input->dev.parent = &device->dev; | ||
699 | input->evbit[0] = BIT(EV_KEY); | ||
700 | set_bit(KEY_BRIGHTNESSUP, input->keybit); | ||
701 | set_bit(KEY_BRIGHTNESSDOWN, input->keybit); | ||
702 | set_bit(KEY_UNKNOWN, input->keybit); | ||
703 | |||
704 | error = input_register_device(input); | ||
705 | if (error) | ||
706 | goto err_free_input_dev; | ||
707 | |||
708 | result = acpi_bus_get_power(fujitsu->acpi_handle, &state); | ||
709 | if (result) { | ||
710 | printk(KERN_ERR "Error reading power state\n"); | ||
711 | goto end; | ||
712 | } | ||
713 | |||
714 | printk(KERN_INFO PREFIX "%s [%s] (%s)\n", | ||
715 | acpi_device_name(device), acpi_device_bid(device), | ||
716 | !device->power.state ? "on" : "off"); | ||
717 | |||
718 | fujitsu->dev = device; | ||
719 | |||
720 | if (ACPI_SUCCESS | ||
721 | (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { | ||
722 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); | ||
723 | if (ACPI_FAILURE | ||
724 | (acpi_evaluate_object | ||
725 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | ||
726 | printk(KERN_ERR "_INI Method failed\n"); | ||
727 | } | ||
728 | |||
729 | /* do config (detect defaults) */ | ||
730 | use_alt_lcd_levels = use_alt_lcd_levels == 1 ? 1 : 0; | ||
731 | disable_brightness_adjust = disable_brightness_adjust == 1 ? 1 : 0; | ||
732 | vdbg_printk(FUJLAPTOP_DBG_INFO, | ||
733 | "config: [alt interface: %d], [adjust disable: %d]\n", | ||
734 | use_alt_lcd_levels, disable_brightness_adjust); | ||
735 | |||
736 | if (get_max_brightness() <= 0) | ||
737 | fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS; | ||
738 | get_lcd_level(); | ||
739 | |||
740 | return result; | ||
741 | |||
742 | end: | ||
743 | err_free_input_dev: | ||
744 | input_free_device(input); | ||
745 | err_uninstall_notify: | ||
746 | acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, | ||
747 | acpi_fujitsu_notify); | ||
748 | err_stop: | ||
749 | |||
750 | return result; | ||
751 | } | ||
752 | |||
753 | static int acpi_fujitsu_remove(struct acpi_device *device, int type) | ||
754 | { | ||
755 | acpi_status status; | ||
756 | struct fujitsu_t *fujitsu = NULL; | ||
757 | |||
758 | if (!device || !acpi_driver_data(device)) | ||
759 | return -EINVAL; | ||
760 | |||
761 | fujitsu = acpi_driver_data(device); | ||
762 | |||
763 | status = acpi_remove_notify_handler(fujitsu->acpi_handle, | ||
764 | ACPI_DEVICE_NOTIFY, | ||
765 | acpi_fujitsu_notify); | ||
766 | |||
767 | if (!device || !acpi_driver_data(device)) | ||
768 | return -EINVAL; | ||
769 | |||
770 | fujitsu->acpi_handle = NULL; | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | /* Brightness notify */ | ||
776 | |||
777 | static void acpi_fujitsu_notify(acpi_handle handle, u32 event, void *data) | ||
778 | { | ||
779 | struct input_dev *input; | ||
780 | int keycode; | ||
781 | int oldb, newb; | ||
782 | |||
783 | input = fujitsu->input; | ||
784 | |||
785 | switch (event) { | ||
786 | case ACPI_FUJITSU_NOTIFY_CODE1: | ||
787 | keycode = 0; | ||
788 | oldb = fujitsu->brightness_level; | ||
789 | get_lcd_level(); | ||
790 | newb = fujitsu->brightness_level; | ||
791 | |||
792 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
793 | "brightness button event [%i -> %i (%i)]\n", | ||
794 | oldb, newb, fujitsu->brightness_changed); | ||
795 | |||
796 | if (oldb < newb) { | ||
797 | if (disable_brightness_adjust != 1) { | ||
798 | if (use_alt_lcd_levels) | ||
799 | set_lcd_level_alt(newb); | ||
800 | else | ||
801 | set_lcd_level(newb); | ||
802 | } | ||
803 | acpi_bus_generate_proc_event(fujitsu->dev, | ||
804 | ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0); | ||
805 | keycode = KEY_BRIGHTNESSUP; | ||
806 | } else if (oldb > newb) { | ||
807 | if (disable_brightness_adjust != 1) { | ||
808 | if (use_alt_lcd_levels) | ||
809 | set_lcd_level_alt(newb); | ||
810 | else | ||
811 | set_lcd_level(newb); | ||
812 | } | ||
813 | acpi_bus_generate_proc_event(fujitsu->dev, | ||
814 | ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0); | ||
815 | keycode = KEY_BRIGHTNESSDOWN; | ||
816 | } | ||
817 | break; | ||
818 | default: | ||
819 | keycode = KEY_UNKNOWN; | ||
820 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
821 | "unsupported event [0x%x]\n", event); | ||
822 | break; | ||
823 | } | ||
824 | |||
825 | if (keycode != 0) { | ||
826 | input_report_key(input, keycode, 1); | ||
827 | input_sync(input); | ||
828 | input_report_key(input, keycode, 0); | ||
829 | input_sync(input); | ||
830 | } | ||
831 | |||
832 | return; | ||
833 | } | ||
834 | |||
835 | /* ACPI device for hotkey handling */ | ||
836 | |||
837 | static int acpi_fujitsu_hotkey_add(struct acpi_device *device) | ||
838 | { | ||
839 | acpi_status status; | ||
840 | acpi_handle handle; | ||
841 | int result = 0; | ||
842 | int state = 0; | ||
843 | struct input_dev *input; | ||
844 | int error; | ||
845 | int i; | ||
846 | |||
847 | if (!device) | ||
848 | return -EINVAL; | ||
849 | |||
850 | fujitsu_hotkey->acpi_handle = device->handle; | ||
851 | sprintf(acpi_device_name(device), "%s", | ||
852 | ACPI_FUJITSU_HOTKEY_DEVICE_NAME); | ||
853 | sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS); | ||
854 | device->driver_data = fujitsu_hotkey; | ||
855 | |||
856 | status = acpi_install_notify_handler(device->handle, | ||
857 | ACPI_DEVICE_NOTIFY, | ||
858 | acpi_fujitsu_hotkey_notify, | ||
859 | fujitsu_hotkey); | ||
860 | |||
861 | if (ACPI_FAILURE(status)) { | ||
862 | printk(KERN_ERR "Error installing notify handler\n"); | ||
863 | error = -ENODEV; | ||
864 | goto err_stop; | ||
865 | } | ||
866 | |||
867 | /* kfifo */ | ||
868 | spin_lock_init(&fujitsu_hotkey->fifo_lock); | ||
869 | fujitsu_hotkey->fifo = | ||
870 | kfifo_alloc(RINGBUFFERSIZE * sizeof(int), GFP_KERNEL, | ||
871 | &fujitsu_hotkey->fifo_lock); | ||
872 | if (IS_ERR(fujitsu_hotkey->fifo)) { | ||
873 | printk(KERN_ERR "kfifo_alloc failed\n"); | ||
874 | error = PTR_ERR(fujitsu_hotkey->fifo); | ||
875 | goto err_stop; | ||
876 | } | ||
877 | |||
878 | fujitsu_hotkey->input = input = input_allocate_device(); | ||
879 | if (!input) { | ||
880 | error = -ENOMEM; | ||
881 | goto err_uninstall_notify; | ||
882 | } | ||
883 | |||
884 | snprintf(fujitsu_hotkey->phys, sizeof(fujitsu_hotkey->phys), | ||
885 | "%s/video/input0", acpi_device_hid(device)); | ||
886 | |||
887 | input->name = acpi_device_name(device); | ||
888 | input->phys = fujitsu_hotkey->phys; | ||
889 | input->id.bustype = BUS_HOST; | ||
890 | input->id.product = 0x06; | ||
891 | input->dev.parent = &device->dev; | ||
892 | |||
893 | set_bit(EV_KEY, input->evbit); | ||
894 | set_bit(fujitsu->keycode1, input->keybit); | ||
895 | set_bit(fujitsu->keycode2, input->keybit); | ||
896 | set_bit(fujitsu->keycode3, input->keybit); | ||
897 | set_bit(fujitsu->keycode4, input->keybit); | ||
898 | set_bit(KEY_UNKNOWN, input->keybit); | ||
899 | |||
900 | error = input_register_device(input); | ||
901 | if (error) | ||
902 | goto err_free_input_dev; | ||
903 | |||
904 | result = acpi_bus_get_power(fujitsu_hotkey->acpi_handle, &state); | ||
905 | if (result) { | ||
906 | printk(KERN_ERR "Error reading power state\n"); | ||
907 | goto end; | ||
908 | } | ||
909 | |||
910 | printk(KERN_INFO PREFIX "%s [%s] (%s)\n", | ||
911 | acpi_device_name(device), acpi_device_bid(device), | ||
912 | !device->power.state ? "on" : "off"); | ||
913 | |||
914 | fujitsu_hotkey->dev = device; | ||
915 | |||
916 | if (ACPI_SUCCESS | ||
917 | (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { | ||
918 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n"); | ||
919 | if (ACPI_FAILURE | ||
920 | (acpi_evaluate_object | ||
921 | (device->handle, METHOD_NAME__INI, NULL, NULL))) | ||
922 | printk(KERN_ERR "_INI Method failed\n"); | ||
923 | } | ||
924 | |||
925 | i = 0; | ||
926 | while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 | ||
927 | && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) | ||
928 | ; /* No action, result is discarded */ | ||
929 | vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i); | ||
930 | |||
931 | fujitsu_hotkey->rfkill_state = | ||
932 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); | ||
933 | |||
934 | /* Suspect this is a keymap of the application panel, print it */ | ||
935 | printk(KERN_INFO "fujitsu-laptop: BTNI: [0x%x]\n", | ||
936 | call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); | ||
937 | |||
938 | #ifdef CONFIG_LEDS_CLASS | ||
939 | if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { | ||
940 | result = led_classdev_register(&fujitsu->pf_device->dev, | ||
941 | &logolamp_led); | ||
942 | if (result == 0) { | ||
943 | fujitsu_hotkey->logolamp_registered = 1; | ||
944 | } else { | ||
945 | printk(KERN_ERR "fujitsu-laptop: Could not register " | ||
946 | "LED handler for logo lamp, error %i\n", result); | ||
947 | } | ||
948 | } | ||
949 | |||
950 | if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && | ||
951 | (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { | ||
952 | result = led_classdev_register(&fujitsu->pf_device->dev, | ||
953 | &kblamps_led); | ||
954 | if (result == 0) { | ||
955 | fujitsu_hotkey->kblamps_registered = 1; | ||
956 | } else { | ||
957 | printk(KERN_ERR "fujitsu-laptop: Could not register " | ||
958 | "LED handler for keyboard lamps, error %i\n", result); | ||
959 | } | ||
960 | } | ||
961 | #endif | ||
962 | |||
963 | return result; | ||
964 | |||
965 | end: | ||
966 | err_free_input_dev: | ||
967 | input_free_device(input); | ||
968 | err_uninstall_notify: | ||
969 | acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, | ||
970 | acpi_fujitsu_hotkey_notify); | ||
971 | kfifo_free(fujitsu_hotkey->fifo); | ||
972 | err_stop: | ||
973 | |||
974 | return result; | ||
975 | } | ||
976 | |||
977 | static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) | ||
978 | { | ||
979 | acpi_status status; | ||
980 | struct fujitsu_hotkey_t *fujitsu_hotkey = NULL; | ||
981 | |||
982 | if (!device || !acpi_driver_data(device)) | ||
983 | return -EINVAL; | ||
984 | |||
985 | fujitsu_hotkey = acpi_driver_data(device); | ||
986 | |||
987 | status = acpi_remove_notify_handler(fujitsu_hotkey->acpi_handle, | ||
988 | ACPI_DEVICE_NOTIFY, | ||
989 | acpi_fujitsu_hotkey_notify); | ||
990 | |||
991 | fujitsu_hotkey->acpi_handle = NULL; | ||
992 | |||
993 | kfifo_free(fujitsu_hotkey->fifo); | ||
994 | |||
995 | return 0; | ||
996 | } | ||
997 | |||
998 | static void acpi_fujitsu_hotkey_notify(acpi_handle handle, u32 event, | ||
999 | void *data) | ||
1000 | { | ||
1001 | struct input_dev *input; | ||
1002 | int keycode, keycode_r; | ||
1003 | unsigned int irb = 1; | ||
1004 | int i, status; | ||
1005 | |||
1006 | input = fujitsu_hotkey->input; | ||
1007 | |||
1008 | fujitsu_hotkey->rfkill_state = | ||
1009 | call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0); | ||
1010 | |||
1011 | switch (event) { | ||
1012 | case ACPI_FUJITSU_NOTIFY_CODE1: | ||
1013 | i = 0; | ||
1014 | while ((irb = | ||
1015 | call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 | ||
1016 | && (i++) < MAX_HOTKEY_RINGBUFFER_SIZE) { | ||
1017 | switch (irb & 0x4ff) { | ||
1018 | case KEY1_CODE: | ||
1019 | keycode = fujitsu->keycode1; | ||
1020 | break; | ||
1021 | case KEY2_CODE: | ||
1022 | keycode = fujitsu->keycode2; | ||
1023 | break; | ||
1024 | case KEY3_CODE: | ||
1025 | keycode = fujitsu->keycode3; | ||
1026 | break; | ||
1027 | case KEY4_CODE: | ||
1028 | keycode = fujitsu->keycode4; | ||
1029 | break; | ||
1030 | case 0: | ||
1031 | keycode = 0; | ||
1032 | break; | ||
1033 | default: | ||
1034 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
1035 | "Unknown GIRB result [%x]\n", irb); | ||
1036 | keycode = -1; | ||
1037 | break; | ||
1038 | } | ||
1039 | if (keycode > 0) { | ||
1040 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
1041 | "Push keycode into ringbuffer [%d]\n", | ||
1042 | keycode); | ||
1043 | status = kfifo_put(fujitsu_hotkey->fifo, | ||
1044 | (unsigned char *)&keycode, | ||
1045 | sizeof(keycode)); | ||
1046 | if (status != sizeof(keycode)) { | ||
1047 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
1048 | "Could not push keycode [0x%x]\n", | ||
1049 | keycode); | ||
1050 | } else { | ||
1051 | input_report_key(input, keycode, 1); | ||
1052 | input_sync(input); | ||
1053 | } | ||
1054 | } else if (keycode == 0) { | ||
1055 | while ((status = | ||
1056 | kfifo_get | ||
1057 | (fujitsu_hotkey->fifo, (unsigned char *) | ||
1058 | &keycode_r, | ||
1059 | sizeof | ||
1060 | (keycode_r))) == sizeof(keycode_r)) { | ||
1061 | input_report_key(input, keycode_r, 0); | ||
1062 | input_sync(input); | ||
1063 | vdbg_printk(FUJLAPTOP_DBG_TRACE, | ||
1064 | "Pop keycode from ringbuffer [%d]\n", | ||
1065 | keycode_r); | ||
1066 | } | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | break; | ||
1071 | default: | ||
1072 | keycode = KEY_UNKNOWN; | ||
1073 | vdbg_printk(FUJLAPTOP_DBG_WARN, | ||
1074 | "Unsupported event [0x%x]\n", event); | ||
1075 | input_report_key(input, keycode, 1); | ||
1076 | input_sync(input); | ||
1077 | input_report_key(input, keycode, 0); | ||
1078 | input_sync(input); | ||
1079 | break; | ||
1080 | } | ||
1081 | |||
1082 | return; | ||
1083 | } | ||
1084 | |||
1085 | /* Initialization */ | ||
1086 | |||
1087 | static const struct acpi_device_id fujitsu_device_ids[] = { | ||
1088 | {ACPI_FUJITSU_HID, 0}, | ||
1089 | {"", 0}, | ||
1090 | }; | ||
1091 | |||
1092 | static struct acpi_driver acpi_fujitsu_driver = { | ||
1093 | .name = ACPI_FUJITSU_DRIVER_NAME, | ||
1094 | .class = ACPI_FUJITSU_CLASS, | ||
1095 | .ids = fujitsu_device_ids, | ||
1096 | .ops = { | ||
1097 | .add = acpi_fujitsu_add, | ||
1098 | .remove = acpi_fujitsu_remove, | ||
1099 | }, | ||
1100 | }; | ||
1101 | |||
1102 | static const struct acpi_device_id fujitsu_hotkey_device_ids[] = { | ||
1103 | {ACPI_FUJITSU_HOTKEY_HID, 0}, | ||
1104 | {"", 0}, | ||
1105 | }; | ||
1106 | |||
1107 | static struct acpi_driver acpi_fujitsu_hotkey_driver = { | ||
1108 | .name = ACPI_FUJITSU_HOTKEY_DRIVER_NAME, | ||
1109 | .class = ACPI_FUJITSU_CLASS, | ||
1110 | .ids = fujitsu_hotkey_device_ids, | ||
1111 | .ops = { | ||
1112 | .add = acpi_fujitsu_hotkey_add, | ||
1113 | .remove = acpi_fujitsu_hotkey_remove, | ||
1114 | }, | ||
1115 | }; | ||
1116 | |||
1117 | static int __init fujitsu_init(void) | ||
1118 | { | ||
1119 | int ret, result, max_brightness; | ||
1120 | |||
1121 | if (acpi_disabled) | ||
1122 | return -ENODEV; | ||
1123 | |||
1124 | fujitsu = kmalloc(sizeof(struct fujitsu_t), GFP_KERNEL); | ||
1125 | if (!fujitsu) | ||
1126 | return -ENOMEM; | ||
1127 | memset(fujitsu, 0, sizeof(struct fujitsu_t)); | ||
1128 | fujitsu->keycode1 = KEY_PROG1; | ||
1129 | fujitsu->keycode2 = KEY_PROG2; | ||
1130 | fujitsu->keycode3 = KEY_PROG3; | ||
1131 | fujitsu->keycode4 = KEY_PROG4; | ||
1132 | dmi_check_system(fujitsu_dmi_table); | ||
1133 | |||
1134 | result = acpi_bus_register_driver(&acpi_fujitsu_driver); | ||
1135 | if (result < 0) { | ||
1136 | ret = -ENODEV; | ||
1137 | goto fail_acpi; | ||
1138 | } | ||
1139 | |||
1140 | /* Register platform stuff */ | ||
1141 | |||
1142 | fujitsu->pf_device = platform_device_alloc("fujitsu-laptop", -1); | ||
1143 | if (!fujitsu->pf_device) { | ||
1144 | ret = -ENOMEM; | ||
1145 | goto fail_platform_driver; | ||
1146 | } | ||
1147 | |||
1148 | ret = platform_device_add(fujitsu->pf_device); | ||
1149 | if (ret) | ||
1150 | goto fail_platform_device1; | ||
1151 | |||
1152 | ret = | ||
1153 | sysfs_create_group(&fujitsu->pf_device->dev.kobj, | ||
1154 | &fujitsupf_attribute_group); | ||
1155 | if (ret) | ||
1156 | goto fail_platform_device2; | ||
1157 | |||
1158 | /* Register backlight stuff */ | ||
1159 | |||
1160 | if (!acpi_video_backlight_support()) { | ||
1161 | fujitsu->bl_device = | ||
1162 | backlight_device_register("fujitsu-laptop", NULL, NULL, | ||
1163 | &fujitsubl_ops); | ||
1164 | if (IS_ERR(fujitsu->bl_device)) | ||
1165 | return PTR_ERR(fujitsu->bl_device); | ||
1166 | max_brightness = fujitsu->max_brightness; | ||
1167 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | ||
1168 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | ||
1169 | } | ||
1170 | |||
1171 | ret = platform_driver_register(&fujitsupf_driver); | ||
1172 | if (ret) | ||
1173 | goto fail_backlight; | ||
1174 | |||
1175 | /* Register hotkey driver */ | ||
1176 | |||
1177 | fujitsu_hotkey = kmalloc(sizeof(struct fujitsu_hotkey_t), GFP_KERNEL); | ||
1178 | if (!fujitsu_hotkey) { | ||
1179 | ret = -ENOMEM; | ||
1180 | goto fail_hotkey; | ||
1181 | } | ||
1182 | memset(fujitsu_hotkey, 0, sizeof(struct fujitsu_hotkey_t)); | ||
1183 | |||
1184 | result = acpi_bus_register_driver(&acpi_fujitsu_hotkey_driver); | ||
1185 | if (result < 0) { | ||
1186 | ret = -ENODEV; | ||
1187 | goto fail_hotkey1; | ||
1188 | } | ||
1189 | |||
1190 | /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ | ||
1191 | |||
1192 | if (!acpi_video_backlight_support()) { | ||
1193 | if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) | ||
1194 | fujitsu->bl_device->props.power = 4; | ||
1195 | else | ||
1196 | fujitsu->bl_device->props.power = 0; | ||
1197 | } | ||
1198 | |||
1199 | printk(KERN_INFO "fujitsu-laptop: driver " FUJITSU_DRIVER_VERSION | ||
1200 | " successfully loaded.\n"); | ||
1201 | |||
1202 | return 0; | ||
1203 | |||
1204 | fail_hotkey1: | ||
1205 | |||
1206 | kfree(fujitsu_hotkey); | ||
1207 | |||
1208 | fail_hotkey: | ||
1209 | |||
1210 | platform_driver_unregister(&fujitsupf_driver); | ||
1211 | |||
1212 | fail_backlight: | ||
1213 | |||
1214 | if (fujitsu->bl_device) | ||
1215 | backlight_device_unregister(fujitsu->bl_device); | ||
1216 | |||
1217 | fail_platform_device2: | ||
1218 | |||
1219 | platform_device_del(fujitsu->pf_device); | ||
1220 | |||
1221 | fail_platform_device1: | ||
1222 | |||
1223 | platform_device_put(fujitsu->pf_device); | ||
1224 | |||
1225 | fail_platform_driver: | ||
1226 | |||
1227 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | ||
1228 | |||
1229 | fail_acpi: | ||
1230 | |||
1231 | kfree(fujitsu); | ||
1232 | |||
1233 | return ret; | ||
1234 | } | ||
1235 | |||
1236 | static void __exit fujitsu_cleanup(void) | ||
1237 | { | ||
1238 | #ifdef CONFIG_LEDS_CLASS | ||
1239 | if (fujitsu_hotkey->logolamp_registered != 0) | ||
1240 | led_classdev_unregister(&logolamp_led); | ||
1241 | |||
1242 | if (fujitsu_hotkey->kblamps_registered != 0) | ||
1243 | led_classdev_unregister(&kblamps_led); | ||
1244 | #endif | ||
1245 | |||
1246 | sysfs_remove_group(&fujitsu->pf_device->dev.kobj, | ||
1247 | &fujitsupf_attribute_group); | ||
1248 | platform_device_unregister(fujitsu->pf_device); | ||
1249 | platform_driver_unregister(&fujitsupf_driver); | ||
1250 | if (fujitsu->bl_device) | ||
1251 | backlight_device_unregister(fujitsu->bl_device); | ||
1252 | |||
1253 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | ||
1254 | |||
1255 | kfree(fujitsu); | ||
1256 | |||
1257 | acpi_bus_unregister_driver(&acpi_fujitsu_hotkey_driver); | ||
1258 | |||
1259 | kfree(fujitsu_hotkey); | ||
1260 | |||
1261 | printk(KERN_INFO "fujitsu-laptop: driver unloaded.\n"); | ||
1262 | } | ||
1263 | |||
1264 | module_init(fujitsu_init); | ||
1265 | module_exit(fujitsu_cleanup); | ||
1266 | |||
1267 | module_param(use_alt_lcd_levels, uint, 0644); | ||
1268 | MODULE_PARM_DESC(use_alt_lcd_levels, | ||
1269 | "Use alternative interface for lcd_levels (needed for Lifebook s6410)."); | ||
1270 | module_param(disable_brightness_adjust, uint, 0644); | ||
1271 | MODULE_PARM_DESC(disable_brightness_adjust, "Disable brightness adjustment ."); | ||
1272 | #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG | ||
1273 | module_param_named(debug, dbg_level, uint, 0644); | ||
1274 | MODULE_PARM_DESC(debug, "Sets debug level bit-mask"); | ||
1275 | #endif | ||
1276 | |||
1277 | MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon"); | ||
1278 | MODULE_DESCRIPTION("Fujitsu laptop extras support"); | ||
1279 | MODULE_VERSION(FUJITSU_DRIVER_VERSION); | ||
1280 | MODULE_LICENSE("GPL"); | ||
1281 | |||
1282 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1D3:*:cvrS6410:*"); | ||
1283 | MODULE_ALIAS("dmi:*:svnFUJITSUSIEMENS:*:pvr:rvnFUJITSU:rnFJNB1E6:*:cvrS6420:*"); | ||
1284 | MODULE_ALIAS("dmi:*:svnFUJITSU:*:pvr:rvnFUJITSU:rnFJNB19C:*:cvrS7020:*"); | ||
1285 | |||
1286 | static struct pnp_device_id pnp_ids[] = { | ||
1287 | {.id = "FUJ02bf"}, | ||
1288 | {.id = "FUJ02B1"}, | ||
1289 | {.id = "FUJ02E3"}, | ||
1290 | {.id = ""} | ||
1291 | }; | ||
1292 | |||
1293 | MODULE_DEVICE_TABLE(pnp, pnp_ids); | ||