diff options
author | Borislav Deianov <borislav@users.sf.net> | 2005-08-17 00:00:00 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2005-08-25 17:37:51 -0400 |
commit | 78f81cc4355c31c798564ff7efb253cc4cdce6c0 (patch) | |
tree | e3be9c36048b75db33218e49e7f75fea8be09261 | |
parent | 9c2c38a122cc23d6a09b8004d60a33913683eedf (diff) |
[ACPI] IBM ThinkPad ACPI Extras Driver v0.12
http://ibm-acpi.sf.net/
Signed-off-by: Borislav Deianov <borislav@users.sf.net>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | Documentation/ibm-acpi.txt | 376 | ||||
-rw-r--r-- | drivers/acpi/ibm_acpi.c | 1598 |
2 files changed, 1442 insertions, 532 deletions
diff --git a/Documentation/ibm-acpi.txt b/Documentation/ibm-acpi.txt index c437b1aeff55..8b3fd82b2ce7 100644 --- a/Documentation/ibm-acpi.txt +++ b/Documentation/ibm-acpi.txt | |||
@@ -1,16 +1,16 @@ | |||
1 | IBM ThinkPad ACPI Extras Driver | 1 | IBM ThinkPad ACPI Extras Driver |
2 | 2 | ||
3 | Version 0.8 | 3 | Version 0.12 |
4 | 8 November 2004 | 4 | 17 August 2005 |
5 | 5 | ||
6 | Borislav Deianov <borislav@users.sf.net> | 6 | Borislav Deianov <borislav@users.sf.net> |
7 | http://ibm-acpi.sf.net/ | 7 | http://ibm-acpi.sf.net/ |
8 | 8 | ||
9 | 9 | ||
10 | This is a Linux ACPI driver for the IBM ThinkPad laptops. It aims to | 10 | This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports |
11 | support various features of these laptops which are accessible through | 11 | various features of these laptops which are accessible through the |
12 | the ACPI framework but not otherwise supported by the generic Linux | 12 | ACPI framework but not otherwise supported by the generic Linux ACPI |
13 | ACPI drivers. | 13 | drivers. |
14 | 14 | ||
15 | 15 | ||
16 | Status | 16 | Status |
@@ -25,9 +25,14 @@ detailed description): | |||
25 | - ThinkLight on and off | 25 | - ThinkLight on and off |
26 | - limited docking and undocking | 26 | - limited docking and undocking |
27 | - UltraBay eject | 27 | - UltraBay eject |
28 | - Experimental: CMOS control | 28 | - CMOS control |
29 | - Experimental: LED control | 29 | - LED control |
30 | - Experimental: ACPI sounds | 30 | - ACPI sounds |
31 | - temperature sensors | ||
32 | - Experimental: embedded controller register dump | ||
33 | - Experimental: LCD brightness control | ||
34 | - Experimental: volume control | ||
35 | - Experimental: fan speed, fan enable/disable | ||
31 | 36 | ||
32 | A compatibility table by model and feature is maintained on the web | 37 | A compatibility table by model and feature is maintained on the web |
33 | site, http://ibm-acpi.sf.net/. I appreciate any success or failure | 38 | site, http://ibm-acpi.sf.net/. I appreciate any success or failure |
@@ -91,12 +96,12 @@ driver is still in the alpha stage, the exact proc file format and | |||
91 | commands supported by the various features is guaranteed to change | 96 | commands supported by the various features is guaranteed to change |
92 | frequently. | 97 | frequently. |
93 | 98 | ||
94 | Driver Version -- /proc/acpi/ibm/driver | 99 | Driver version -- /proc/acpi/ibm/driver |
95 | -------------------------------------- | 100 | --------------------------------------- |
96 | 101 | ||
97 | The driver name and version. No commands can be written to this file. | 102 | The driver name and version. No commands can be written to this file. |
98 | 103 | ||
99 | Hot Keys -- /proc/acpi/ibm/hotkey | 104 | Hot keys -- /proc/acpi/ibm/hotkey |
100 | --------------------------------- | 105 | --------------------------------- |
101 | 106 | ||
102 | Without this driver, only the Fn-F4 key (sleep button) generates an | 107 | Without this driver, only the Fn-F4 key (sleep button) generates an |
@@ -188,7 +193,7 @@ and, on the X40, video corruption. By disabling automatic switching, | |||
188 | the flickering or video corruption can be avoided. | 193 | the flickering or video corruption can be avoided. |
189 | 194 | ||
190 | The video_switch command cycles through the available video outputs | 195 | The video_switch command cycles through the available video outputs |
191 | (it sumulates the behavior of Fn-F7). | 196 | (it simulates the behavior of Fn-F7). |
192 | 197 | ||
193 | Video expansion can be toggled through this feature. This controls | 198 | Video expansion can be toggled through this feature. This controls |
194 | whether the display is expanded to fill the entire LCD screen when a | 199 | whether the display is expanded to fill the entire LCD screen when a |
@@ -201,6 +206,12 @@ Fn-F7 from working. This also disables the video output switching | |||
201 | features of this driver, as it uses the same ACPI methods as | 206 | features of this driver, as it uses the same ACPI methods as |
202 | Fn-F7. Video switching on the console should still work. | 207 | Fn-F7. Video switching on the console should still work. |
203 | 208 | ||
209 | UPDATE: There's now a patch for the X.org Radeon driver which | ||
210 | addresses this issue. Some people are reporting success with the patch | ||
211 | while others are still having problems. For more information: | ||
212 | |||
213 | https://bugs.freedesktop.org/show_bug.cgi?id=2000 | ||
214 | |||
204 | ThinkLight control -- /proc/acpi/ibm/light | 215 | ThinkLight control -- /proc/acpi/ibm/light |
205 | ------------------------------------------ | 216 | ------------------------------------------ |
206 | 217 | ||
@@ -211,7 +222,7 @@ models which do not make the status available will show it as | |||
211 | echo on > /proc/acpi/ibm/light | 222 | echo on > /proc/acpi/ibm/light |
212 | echo off > /proc/acpi/ibm/light | 223 | echo off > /proc/acpi/ibm/light |
213 | 224 | ||
214 | Docking / Undocking -- /proc/acpi/ibm/dock | 225 | Docking / undocking -- /proc/acpi/ibm/dock |
215 | ------------------------------------------ | 226 | ------------------------------------------ |
216 | 227 | ||
217 | Docking and undocking (e.g. with the X4 UltraBase) requires some | 228 | Docking and undocking (e.g. with the X4 UltraBase) requires some |
@@ -228,11 +239,15 @@ NOTE: These events will only be generated if the laptop was docked | |||
228 | when originally booted. This is due to the current lack of support for | 239 | when originally booted. This is due to the current lack of support for |
229 | hot plugging of devices in the Linux ACPI framework. If the laptop was | 240 | hot plugging of devices in the Linux ACPI framework. If the laptop was |
230 | booted while not in the dock, the following message is shown in the | 241 | booted while not in the dock, the following message is shown in the |
231 | logs: "ibm_acpi: dock device not present". No dock-related events are | 242 | logs: |
232 | generated but the dock and undock commands described below still | 243 | |
233 | work. They can be executed manually or triggered by Fn key | 244 | Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present |
234 | combinations (see the example acpid configuration files included in | 245 | |
235 | the driver tarball package available on the web site). | 246 | In this case, no dock-related events are generated but the dock and |
247 | undock commands described below still work. They can be executed | ||
248 | manually or triggered by Fn key combinations (see the example acpid | ||
249 | configuration files included in the driver tarball package available | ||
250 | on the web site). | ||
236 | 251 | ||
237 | When the eject request button on the dock is pressed, the first event | 252 | When the eject request button on the dock is pressed, the first event |
238 | above is generated. The handler for this event should issue the | 253 | above is generated. The handler for this event should issue the |
@@ -267,7 +282,7 @@ the only docking stations currently supported are the X-series | |||
267 | UltraBase docks and "dumb" port replicators like the Mini Dock (the | 282 | UltraBase docks and "dumb" port replicators like the Mini Dock (the |
268 | latter don't need any ACPI support, actually). | 283 | latter don't need any ACPI support, actually). |
269 | 284 | ||
270 | UltraBay Eject -- /proc/acpi/ibm/bay | 285 | UltraBay eject -- /proc/acpi/ibm/bay |
271 | ------------------------------------ | 286 | ------------------------------------ |
272 | 287 | ||
273 | Inserting or ejecting an UltraBay device requires some actions to be | 288 | Inserting or ejecting an UltraBay device requires some actions to be |
@@ -284,8 +299,11 @@ when the laptop was originally booted (on the X series, the UltraBay | |||
284 | is in the dock, so it may not be present if the laptop was undocked). | 299 | is in the dock, so it may not be present if the laptop was undocked). |
285 | This is due to the current lack of support for hot plugging of devices | 300 | This is due to the current lack of support for hot plugging of devices |
286 | in the Linux ACPI framework. If the laptop was booted without the | 301 | in the Linux ACPI framework. If the laptop was booted without the |
287 | UltraBay, the following message is shown in the logs: "ibm_acpi: bay | 302 | UltraBay, the following message is shown in the logs: |
288 | device not present". No bay-related events are generated but the eject | 303 | |
304 | Mar 17 01:42:34 aero kernel: ibm_acpi: bay device not present | ||
305 | |||
306 | In this case, no bay-related events are generated but the eject | ||
289 | command described below still works. It can be executed manually or | 307 | command described below still works. It can be executed manually or |
290 | triggered by a hot key combination. | 308 | triggered by a hot key combination. |
291 | 309 | ||
@@ -306,22 +324,33 @@ necessary to enable the UltraBay device (e.g. call idectl). | |||
306 | The contents of the /proc/acpi/ibm/bay file shows the current status | 324 | The contents of the /proc/acpi/ibm/bay file shows the current status |
307 | of the UltraBay, as provided by the ACPI framework. | 325 | of the UltraBay, as provided by the ACPI framework. |
308 | 326 | ||
309 | Experimental Features | 327 | EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use |
310 | --------------------- | 328 | this feature, you need to supply the experimental=1 parameter when |
329 | loading the module): | ||
330 | |||
331 | These models do not have a button near the UltraBay device to request | ||
332 | a hot eject but rather require the laptop to be put to sleep | ||
333 | (suspend-to-ram) before the bay device is ejected or inserted). | ||
334 | The sequence of steps to eject the device is as follows: | ||
335 | |||
336 | echo eject > /proc/acpi/ibm/bay | ||
337 | put the ThinkPad to sleep | ||
338 | remove the drive | ||
339 | resume from sleep | ||
340 | cat /proc/acpi/ibm/bay should show that the drive was removed | ||
341 | |||
342 | On the A3x, both the UltraBay 2000 and UltraBay Plus devices are | ||
343 | supported. Use "eject2" instead of "eject" for the second bay. | ||
311 | 344 | ||
312 | The following features are marked experimental because using them | 345 | Note: the UltraBay eject support on the 600e/x, A22p and A3x is |
313 | involves guessing the correct values of some parameters. Guessing | 346 | EXPERIMENTAL and may not work as expected. USE WITH CAUTION! |
314 | incorrectly may have undesirable effects like crashing your | ||
315 | ThinkPad. USE THESE WITH CAUTION! To activate them, you'll need to | ||
316 | supply the experimental=1 parameter when loading the module. | ||
317 | 347 | ||
318 | Experimental: CMOS control - /proc/acpi/ibm/cmos | 348 | CMOS control -- /proc/acpi/ibm/cmos |
319 | ------------------------------------------------ | 349 | ----------------------------------- |
320 | 350 | ||
321 | This feature is used internally by the ACPI firmware to control the | 351 | This feature is used internally by the ACPI firmware to control the |
322 | ThinkLight on most newer ThinkPad models. It appears that it can also | 352 | ThinkLight on most newer ThinkPad models. It may also control LCD |
323 | control LCD brightness, sounds volume and more, but only on some | 353 | brightness, sounds volume and more, but only on some models. |
324 | models. | ||
325 | 354 | ||
326 | The commands are non-negative integer numbers: | 355 | The commands are non-negative integer numbers: |
327 | 356 | ||
@@ -330,10 +359,9 @@ The commands are non-negative integer numbers: | |||
330 | echo 2 >/proc/acpi/ibm/cmos | 359 | echo 2 >/proc/acpi/ibm/cmos |
331 | ... | 360 | ... |
332 | 361 | ||
333 | The range of numbers which are used internally by various models is 0 | 362 | The range of valid numbers is 0 to 21, but not all have an effect and |
334 | to 21, but it's possible that numbers outside this range have | 363 | the behavior varies from model to model. Here is the behavior on the |
335 | interesting behavior. Here is the behavior on the X40 (tpb is the | 364 | X40 (tpb is the ThinkPad Buttons utility): |
336 | ThinkPad Buttons utility): | ||
337 | 365 | ||
338 | 0 - no effect but tpb reports "Volume down" | 366 | 0 - no effect but tpb reports "Volume down" |
339 | 1 - no effect but tpb reports "Volume up" | 367 | 1 - no effect but tpb reports "Volume up" |
@@ -346,26 +374,18 @@ ThinkPad Buttons utility): | |||
346 | 13 - ThinkLight off | 374 | 13 - ThinkLight off |
347 | 14 - no effect but tpb reports ThinkLight status change | 375 | 14 - no effect but tpb reports ThinkLight status change |
348 | 376 | ||
349 | If you try this feature, please send me a report similar to the | 377 | LED control -- /proc/acpi/ibm/led |
350 | above. On models which allow control of LCD brightness or sound | 378 | --------------------------------- |
351 | volume, I'd like to provide this functionality in an user-friendly | ||
352 | way, but first I need a way to identify the models which this is | ||
353 | possible. | ||
354 | |||
355 | Experimental: LED control - /proc/acpi/ibm/LED | ||
356 | ---------------------------------------------- | ||
357 | 379 | ||
358 | Some of the LED indicators can be controlled through this feature. The | 380 | Some of the LED indicators can be controlled through this feature. The |
359 | available commands are: | 381 | available commands are: |
360 | 382 | ||
361 | echo <led number> on >/proc/acpi/ibm/led | 383 | echo '<led number> on' >/proc/acpi/ibm/led |
362 | echo <led number> off >/proc/acpi/ibm/led | 384 | echo '<led number> off' >/proc/acpi/ibm/led |
363 | echo <led number> blink >/proc/acpi/ibm/led | 385 | echo '<led number> blink' >/proc/acpi/ibm/led |
364 | 386 | ||
365 | The <led number> parameter is a non-negative integer. The range of LED | 387 | The <led number> range is 0 to 7. The set of LEDs that can be |
366 | numbers used internally by various models is 0 to 7 but it's possible | 388 | controlled varies from model to model. Here is the mapping on the X40: |
367 | that numbers outside this range are also valid. Here is the mapping on | ||
368 | the X40: | ||
369 | 389 | ||
370 | 0 - power | 390 | 0 - power |
371 | 1 - battery (orange) | 391 | 1 - battery (orange) |
@@ -376,49 +396,224 @@ the X40: | |||
376 | 396 | ||
377 | All of the above can be turned on and off and can be made to blink. | 397 | All of the above can be turned on and off and can be made to blink. |
378 | 398 | ||
379 | If you try this feature, please send me a report similar to the | 399 | ACPI sounds -- /proc/acpi/ibm/beep |
380 | above. I'd like to provide this functionality in an user-friendly way, | 400 | ---------------------------------- |
381 | but first I need to identify the which numbers correspond to which | ||
382 | LEDs on various models. | ||
383 | |||
384 | Experimental: ACPI sounds - /proc/acpi/ibm/beep | ||
385 | ----------------------------------------------- | ||
386 | 401 | ||
387 | The BEEP method is used internally by the ACPI firmware to provide | 402 | The BEEP method is used internally by the ACPI firmware to provide |
388 | audible alerts in various situtation. This feature allows the same | 403 | audible alerts in various situations. This feature allows the same |
389 | sounds to be triggered manually. | 404 | sounds to be triggered manually. |
390 | 405 | ||
391 | The commands are non-negative integer numbers: | 406 | The commands are non-negative integer numbers: |
392 | 407 | ||
393 | echo 0 >/proc/acpi/ibm/beep | 408 | echo <number> >/proc/acpi/ibm/beep |
394 | echo 1 >/proc/acpi/ibm/beep | ||
395 | echo 2 >/proc/acpi/ibm/beep | ||
396 | ... | ||
397 | 409 | ||
398 | The range of numbers which are used internally by various models is 0 | 410 | The valid <number> range is 0 to 17. Not all numbers trigger sounds |
399 | to 17, but it's possible that numbers outside this range are also | 411 | and the sounds vary from model to model. Here is the behavior on the |
400 | valid. Here is the behavior on the X40: | 412 | X40: |
401 | 413 | ||
402 | 2 - two beeps, pause, third beep | 414 | 0 - stop a sound in progress (but use 17 to stop 16) |
415 | 2 - two beeps, pause, third beep ("low battery") | ||
403 | 3 - single beep | 416 | 3 - single beep |
404 | 4 - "unable" | 417 | 4 - high, followed by low-pitched beep ("unable") |
405 | 5 - single beep | 418 | 5 - single beep |
406 | 6 - "AC/DC" | 419 | 6 - very high, followed by high-pitched beep ("AC/DC") |
407 | 7 - high-pitched beep | 420 | 7 - high-pitched beep |
408 | 9 - three short beeps | 421 | 9 - three short beeps |
409 | 10 - very long beep | 422 | 10 - very long beep |
410 | 12 - low-pitched beep | 423 | 12 - low-pitched beep |
424 | 15 - three high-pitched beeps repeating constantly, stop with 0 | ||
425 | 16 - one medium-pitched beep repeating constantly, stop with 17 | ||
426 | 17 - stop 16 | ||
427 | |||
428 | Temperature sensors -- /proc/acpi/ibm/thermal | ||
429 | --------------------------------------------- | ||
430 | |||
431 | Most ThinkPads include six or more separate temperature sensors but | ||
432 | only expose the CPU temperature through the standard ACPI methods. | ||
433 | This feature shows readings from up to eight different sensors. Some | ||
434 | readings may not be valid, e.g. may show large negative values. For | ||
435 | example, on the X40, a typical output may be: | ||
436 | |||
437 | temperatures: 42 42 45 41 36 -128 33 -128 | ||
438 | |||
439 | Thomas Gruber took his R51 apart and traced all six active sensors in | ||
440 | his laptop (the location of sensors may vary on other models): | ||
441 | |||
442 | 1: CPU | ||
443 | 2: Mini PCI Module | ||
444 | 3: HDD | ||
445 | 4: GPU | ||
446 | 5: Battery | ||
447 | 6: N/A | ||
448 | 7: Battery | ||
449 | 8: N/A | ||
450 | |||
451 | No commands can be written to this file. | ||
452 | |||
453 | EXPERIMENTAL: Embedded controller reigster dump -- /proc/acpi/ibm/ecdump | ||
454 | ------------------------------------------------------------------------ | ||
455 | |||
456 | This feature is marked EXPERIMENTAL because the implementation | ||
457 | directly accesses hardware registers and may not work as expected. USE | ||
458 | WITH CAUTION! To use this feature, you need to supply the | ||
459 | experimental=1 parameter when loading the module. | ||
460 | |||
461 | This feature dumps the values of 256 embedded controller | ||
462 | registers. Values which have changed since the last time the registers | ||
463 | were dumped are marked with a star: | ||
464 | |||
465 | [root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump | ||
466 | EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f | ||
467 | EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 | ||
468 | EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 | ||
469 | EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 | ||
470 | EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00 | ||
471 | EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 | ||
472 | EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc | ||
473 | EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
474 | EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80 | ||
475 | EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00 | ||
476 | EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
477 | EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00 | ||
478 | EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
479 | EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
480 | EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
481 | EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 | ||
482 | EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a | ||
483 | |||
484 | This feature can be used to determine the register holding the fan | ||
485 | speed on some models. To do that, do the following: | ||
486 | |||
487 | - make sure the battery is fully charged | ||
488 | - make sure the fan is running | ||
489 | - run 'cat /proc/acpi/ibm/ecdump' several times, once per second or so | ||
490 | |||
491 | The first step makes sure various charging-related values don't | ||
492 | vary. The second ensures that the fan-related values do vary, since | ||
493 | the fan speed fluctuates a bit. The third will (hopefully) mark the | ||
494 | fan register with a star: | ||
495 | |||
496 | [root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump | ||
497 | EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f | ||
498 | EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00 | ||
499 | EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00 | ||
500 | EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80 | ||
501 | EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00 | ||
502 | EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00 | ||
503 | EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc | ||
504 | EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
505 | EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80 | ||
506 | EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00 | ||
507 | EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
508 | EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00 | ||
509 | EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
510 | EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
511 | EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ||
512 | EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03 | ||
513 | EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a | ||
514 | |||
515 | Another set of values that varies often is the temperature | ||
516 | readings. Since temperatures don't change vary fast, you can take | ||
517 | several quick dumps to eliminate them. | ||
518 | |||
519 | You can use a similar method to figure out the meaning of other | ||
520 | embedded controller registers - e.g. make sure nothing else changes | ||
521 | except the charging or discharging battery to determine which | ||
522 | registers contain the current battery capacity, etc. If you experiment | ||
523 | with this, do send me your results (including some complete dumps with | ||
524 | a description of the conditions when they were taken.) | ||
525 | |||
526 | EXPERIMENTAL: LCD brightness control -- /proc/acpi/ibm/brightness | ||
527 | ----------------------------------------------------------------- | ||
528 | |||
529 | This feature is marked EXPERIMENTAL because the implementation | ||
530 | directly accesses hardware registers and may not work as expected. USE | ||
531 | WITH CAUTION! To use this feature, you need to supply the | ||
532 | experimental=1 parameter when loading the module. | ||
533 | |||
534 | This feature allows software control of the LCD brightness on ThinkPad | ||
535 | models which don't have a hardware brightness slider. The available | ||
536 | commands are: | ||
537 | |||
538 | echo up >/proc/acpi/ibm/brightness | ||
539 | echo down >/proc/acpi/ibm/brightness | ||
540 | echo 'level <level>' >/proc/acpi/ibm/brightness | ||
541 | |||
542 | The <level> number range is 0 to 7, although not all of them may be | ||
543 | distinct. The current brightness level is shown in the file. | ||
544 | |||
545 | EXPERIMENTAL: Volume control -- /proc/acpi/ibm/volume | ||
546 | ----------------------------------------------------- | ||
547 | |||
548 | This feature is marked EXPERIMENTAL because the implementation | ||
549 | directly accesses hardware registers and may not work as expected. USE | ||
550 | WITH CAUTION! To use this feature, you need to supply the | ||
551 | experimental=1 parameter when loading the module. | ||
552 | |||
553 | This feature allows volume control on ThinkPad models which don't have | ||
554 | a hardware volume knob. The available commands are: | ||
555 | |||
556 | echo up >/proc/acpi/ibm/volume | ||
557 | echo down >/proc/acpi/ibm/volume | ||
558 | echo mute >/proc/acpi/ibm/volume | ||
559 | echo 'level <level>' >/proc/acpi/ibm/volume | ||
560 | |||
561 | The <level> number range is 0 to 15 although not all of them may be | ||
562 | distinct. The unmute the volume after the mute command, use either the | ||
563 | up or down command (the level command will not unmute the volume). | ||
564 | The current volume level and mute state is shown in the file. | ||
565 | |||
566 | EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan | ||
567 | ----------------------------------------------------------------- | ||
568 | |||
569 | This feature is marked EXPERIMENTAL because the implementation | ||
570 | directly accesses hardware registers and may not work as expected. USE | ||
571 | WITH CAUTION! To use this feature, you need to supply the | ||
572 | experimental=1 parameter when loading the module. | ||
573 | |||
574 | This feature attempts to show the current fan speed. The speed is read | ||
575 | directly from the hardware registers of the embedded controller. This | ||
576 | is known to work on later R, T and X series ThinkPads but may show a | ||
577 | bogus value on other models. | ||
578 | |||
579 | The fan may be enabled or disabled with the following commands: | ||
580 | |||
581 | echo enable >/proc/acpi/ibm/fan | ||
582 | echo disable >/proc/acpi/ibm/fan | ||
583 | |||
584 | WARNING WARNING WARNING: do not leave the fan disabled unless you are | ||
585 | monitoring the temperature sensor readings and you are ready to enable | ||
586 | it if necessary to avoid overheating. | ||
587 | |||
588 | The fan only runs if it's enabled *and* the various temperature | ||
589 | sensors which control it read high enough. On the X40, this seems to | ||
590 | depend on the CPU and HDD temperatures. Specifically, the fan is | ||
591 | turned on when either the CPU temperature climbs to 56 degrees or the | ||
592 | HDD temperature climbs to 46 degrees. The fan is turned off when the | ||
593 | CPU temperature drops to 49 degrees and the HDD temperature drops to | ||
594 | 41 degrees. These thresholds cannot currently be controlled. | ||
595 | |||
596 | On the X31 and X40 (and ONLY on those models), the fan speed can be | ||
597 | controlled to a certain degree. Once the fan is running, it can be | ||
598 | forced to run faster or slower with the following command: | ||
599 | |||
600 | echo 'speed <speed>' > /proc/acpi/ibm/thermal | ||
601 | |||
602 | The sustainable range of fan speeds on the X40 appears to be from | ||
603 | about 3700 to about 7350. Values outside this range either do not have | ||
604 | any effect or the fan speed eventually settles somewhere in that | ||
605 | range. The fan cannot be stopped or started with this command. | ||
606 | |||
607 | On the 570, temperature readings are not available through this | ||
608 | feature and the fan control works a little differently. The fan speed | ||
609 | is reported in levels from 0 (off) to 7 (max) and can be controlled | ||
610 | with the following command: | ||
411 | 611 | ||
412 | (I've only been able to identify a couple of them). | 612 | echo 'level <level>' > /proc/acpi/ibm/thermal |
413 | |||
414 | If you try this feature, please send me a report similar to the | ||
415 | above. I'd like to provide this functionality in an user-friendly way, | ||
416 | but first I need to identify the which numbers correspond to which | ||
417 | sounds on various models. | ||
418 | 613 | ||
419 | 614 | ||
420 | Multiple Command, Module Parameters | 615 | Multiple Commands, Module Parameters |
421 | ----------------------------------- | 616 | ------------------------------------ |
422 | 617 | ||
423 | Multiple commands can be written to the proc files in one shot by | 618 | Multiple commands can be written to the proc files in one shot by |
424 | separating them with commas, for example: | 619 | separating them with commas, for example: |
@@ -451,24 +646,19 @@ scripts (included with ibm-acpi for completeness): | |||
451 | /usr/local/sbin/laptop_mode -- from the Linux kernel source | 646 | /usr/local/sbin/laptop_mode -- from the Linux kernel source |
452 | distribution, see Documentation/laptop-mode.txt | 647 | distribution, see Documentation/laptop-mode.txt |
453 | /sbin/service -- comes with Redhat/Fedora distributions | 648 | /sbin/service -- comes with Redhat/Fedora distributions |
649 | /usr/sbin/hibernate -- from the Software Suspend 2 distribution, | ||
650 | see http://softwaresuspend.berlios.de/ | ||
454 | 651 | ||
455 | Toan T Nguyen <ntt@control.uchicago.edu> has written a SuSE powersave | 652 | Toan T Nguyen <ntt@physics.ucla.edu> notes that Suse uses the |
456 | script for the X20, included in config/usr/sbin/ibm_hotkeys_X20 | 653 | powersave program to suspend ('powersave --suspend-to-ram') or |
654 | hibernate ('powersave --suspend-to-disk'). This means that the | ||
655 | hibernate script is not needed on that distribution. | ||
457 | 656 | ||
458 | Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event | 657 | Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event |
459 | handler script for the X31. You can get the latest version from | 658 | handler script for the X31. You can get the latest version from |
460 | http://dev.gentoo.org/~brix/files/x31.sh | 659 | http://dev.gentoo.org/~brix/files/x31.sh |
461 | 660 | ||
462 | David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh | 661 | David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh |
463 | script which works on Debian systems, included in | 662 | script which works on Debian systems. This scripts has now been |
464 | configs/etc/acpi/actions/blank-debian.sh | 663 | extended to also work on Fedora systems and included as the default |
465 | 664 | blank.sh in the distribution. | |
466 | |||
467 | TODO | ||
468 | ---- | ||
469 | |||
470 | I'd like to implement the following features but haven't yet found the | ||
471 | time and/or I don't yet know how to implement them: | ||
472 | |||
473 | - UltraBay floppy drive support | ||
474 | |||
diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c index ad85e10001f4..5cc090326ddc 100644 --- a/drivers/acpi/ibm_acpi.c +++ b/drivers/acpi/ibm_acpi.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * ibm_acpi.c - IBM ThinkPad ACPI Extras | 2 | * ibm_acpi.c - IBM ThinkPad ACPI Extras |
3 | * | 3 | * |
4 | * | 4 | * |
5 | * Copyright (C) 2004 Borislav Deianov | 5 | * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
@@ -17,38 +17,62 @@ | |||
17 | * You should have received a copy of the GNU General Public License | 17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | * | 20 | */ |
21 | |||
22 | #define IBM_VERSION "0.12a" | ||
23 | |||
24 | /* | ||
21 | * Changelog: | 25 | * Changelog: |
22 | * | 26 | * |
23 | * 2004-08-09 0.1 initial release, support for X series | 27 | * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels |
24 | * 2004-08-14 0.2 support for T series, X20 | 28 | * 2005-03-17 0.11 support for 600e, 770x |
25 | * bluetooth enable/disable | 29 | * thanks to Jamie Lentin <lentinj@dial.pipex.com> |
26 | * hotkey events disabled by default | 30 | * support for 770e, G41 |
27 | * removed fan control, currently useless | 31 | * G40 and G41 don't have a thinklight |
28 | * 2004-08-17 0.3 support for R40 | 32 | * temperatures no longer experimental |
29 | * lcd off, brightness control | 33 | * experimental brightness control |
30 | * thinklight on/off | 34 | * experimental volume control |
31 | * 2004-09-16 0.4 support for module parameters | 35 | * experimental fan enable/disable |
32 | * hotkey mask can be prefixed by 0x | 36 | * 2005-01-16 0.10 fix module loading on R30, R31 |
33 | * video output switching | 37 | * 2005-01-16 0.9 support for 570, R30, R31 |
34 | * video expansion control | 38 | * ultrabay support on A22p, A3x |
35 | * ultrabay eject support | 39 | * limit arg for cmos, led, beep, drop experimental status |
36 | * removed lcd brightness/on/off control, didn't work | 40 | * more capable led control on A21e, A22p, T20-22, X20 |
41 | * experimental temperatures and fan speed | ||
42 | * experimental embedded controller register dump | ||
43 | * mark more functions as __init, drop incorrect __exit | ||
44 | * use MODULE_VERSION | ||
45 | * thanks to Henrik Brix Andersen <brix@gentoo.org> | ||
46 | * fix parameter passing on module loading | ||
47 | * thanks to Rusty Russell <rusty@rustcorp.com.au> | ||
48 | * thanks to Jim Radford <radford@blackbean.org> | ||
49 | * 2004-11-08 0.8 fix init error case, don't return from a macro | ||
50 | * thanks to Chris Wright <chrisw@osdl.org> | ||
51 | * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 | ||
52 | * fix led control on A21e | ||
53 | * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device | ||
37 | * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 | 54 | * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 |
38 | * proc file format changed | 55 | * proc file format changed |
39 | * video_switch command | 56 | * video_switch command |
40 | * experimental cmos control | 57 | * experimental cmos control |
41 | * experimental led control | 58 | * experimental led control |
42 | * experimental acpi sounds | 59 | * experimental acpi sounds |
43 | * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device | 60 | * 2004-09-16 0.4 support for module parameters |
44 | * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 | 61 | * hotkey mask can be prefixed by 0x |
45 | * fix LED control on A21e | 62 | * video output switching |
46 | * 2004-11-08 0.8 fix init error case, don't return from a macro | 63 | * video expansion control |
47 | * thanks to Chris Wright <chrisw@osdl.org> | 64 | * ultrabay eject support |
65 | * removed lcd brightness/on/off control, didn't work | ||
66 | * 2004-08-17 0.3 support for R40 | ||
67 | * lcd off, brightness control | ||
68 | * thinklight on/off | ||
69 | * 2004-08-14 0.2 support for T series, X20 | ||
70 | * bluetooth enable/disable | ||
71 | * hotkey events disabled by default | ||
72 | * removed fan control, currently useless | ||
73 | * 2004-08-09 0.1 initial release, support for X series | ||
48 | */ | 74 | */ |
49 | 75 | ||
50 | #define IBM_VERSION "0.8" | ||
51 | |||
52 | #include <linux/kernel.h> | 76 | #include <linux/kernel.h> |
53 | #include <linux/module.h> | 77 | #include <linux/module.h> |
54 | #include <linux/init.h> | 78 | #include <linux/init.h> |
@@ -64,6 +88,11 @@ | |||
64 | #define IBM_FILE "ibm_acpi" | 88 | #define IBM_FILE "ibm_acpi" |
65 | #define IBM_URL "http://ibm-acpi.sf.net/" | 89 | #define IBM_URL "http://ibm-acpi.sf.net/" |
66 | 90 | ||
91 | MODULE_AUTHOR("Borislav Deianov"); | ||
92 | MODULE_DESCRIPTION(IBM_DESC); | ||
93 | MODULE_VERSION(IBM_VERSION); | ||
94 | MODULE_LICENSE("GPL"); | ||
95 | |||
67 | #define IBM_DIR IBM_NAME | 96 | #define IBM_DIR IBM_NAME |
68 | 97 | ||
69 | #define IBM_LOG IBM_FILE ": " | 98 | #define IBM_LOG IBM_FILE ": " |
@@ -84,54 +113,122 @@ static acpi_handle root_handle = NULL; | |||
84 | #define IBM_HANDLE(object, parent, paths...) \ | 113 | #define IBM_HANDLE(object, parent, paths...) \ |
85 | static acpi_handle object##_handle; \ | 114 | static acpi_handle object##_handle; \ |
86 | static acpi_handle *object##_parent = &parent##_handle; \ | 115 | static acpi_handle *object##_parent = &parent##_handle; \ |
116 | static char *object##_path; \ | ||
87 | static char *object##_paths[] = { paths } | 117 | static char *object##_paths[] = { paths } |
88 | 118 | ||
89 | IBM_HANDLE(ec, root, | 119 | /* |
90 | "\\_SB.PCI0.ISA.EC", /* A21e, A22p, T20, T21, X20 */ | 120 | * The following models are supported to various degrees: |
91 | "\\_SB.PCI0.LPC.EC", /* all others */ | 121 | * |
92 | ); | 122 | * 570, 600e, 600x, 770e, 770x |
93 | 123 | * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p | |
94 | IBM_HANDLE(vid, root, | 124 | * G40, G41 |
95 | "\\_SB.PCI0.VID", /* A21e, G40, X30, X40 */ | 125 | * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51 |
96 | "\\_SB.PCI0.AGP.VID", /* all others */ | 126 | * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43 |
97 | ); | 127 | * X20, X21, X22, X23, X24, X30, X31, X40 |
98 | 128 | * | |
99 | IBM_HANDLE(cmos, root, | 129 | * The following models have no supported features: |
100 | "\\UCMS", /* R50, R50p, R51, T4x, X31, X40 */ | 130 | * |
101 | "\\CMOS", /* A3x, G40, R32, T23, T30, X22, X24, X30 */ | 131 | * 240, 240x, i1400 |
102 | "\\CMS", /* R40, R40e */ | 132 | * |
103 | ); /* A21e, A22p, T20, T21, X20 */ | 133 | * Still missing DSDTs for the following models: |
104 | 134 | * | |
105 | IBM_HANDLE(dock, root, | 135 | * A20p, A22e, A22m |
106 | "\\_SB.GDCK", /* X30, X31, X40 */ | 136 | * R52 |
107 | "\\_SB.PCI0.DOCK", /* A22p, T20, T21, X20 */ | 137 | * S31 |
108 | "\\_SB.PCI0.PCI1.DOCK", /* all others */ | 138 | * T43p |
109 | ); /* A21e, G40, R32, R40, R40e */ | 139 | */ |
110 | 140 | ||
111 | IBM_HANDLE(bay, root, | 141 | IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ |
112 | "\\_SB.PCI0.IDE0.SCND.MSTR"); /* all except A21e */ | 142 | "\\_SB.PCI.ISA.EC", /* 570 */ |
113 | IBM_HANDLE(bayej, root, | 143 | "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ |
114 | "\\_SB.PCI0.IDE0.SCND.MSTR._EJ0"); /* all except A2x, A3x */ | 144 | "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ |
115 | 145 | "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ | |
116 | IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A22p, T20, T21, X20 */ | 146 | "\\_SB.PCI0.ICH3.EC0", /* R31 */ |
117 | IBM_HANDLE(hkey, ec, "HKEY"); /* all */ | 147 | "\\_SB.PCI0.LPC.EC", /* all others */ |
118 | IBM_HANDLE(led, ec, "LED"); /* all except A21e, A22p, T20, T21, X20 */ | 148 | ); |
119 | IBM_HANDLE(sysl, ec, "SYSL"); /* A21e, A22p, T20, T21, X20 */ | 149 | |
120 | IBM_HANDLE(bled, ec, "BLED"); /* A22p, T20, T21, X20 */ | 150 | IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ |
121 | IBM_HANDLE(beep, ec, "BEEP"); /* all models */ | 151 | "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ |
152 | "\\_SB.PCI0.VID0", /* 770e */ | ||
153 | "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ | ||
154 | "\\_SB.PCI0.AGP.VID", /* all others */ | ||
155 | ); /* R30, R31 */ | ||
156 | |||
157 | IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ | ||
158 | |||
159 | IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ | ||
160 | "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ | ||
161 | "\\CMS", /* R40, R40e */ | ||
162 | ); /* all others */ | ||
163 | |||
164 | IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ | ||
165 | "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ | ||
166 | "\\_SB.PCI0.PCI1.DOCK", /* all others */ | ||
167 | "\\_SB.PCI.ISA.SLCE", /* 570 */ | ||
168 | ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ | ||
169 | |||
170 | IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ | ||
171 | "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ | ||
172 | "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ | ||
173 | ); /* A21e, R30, R31 */ | ||
174 | |||
175 | IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ | ||
176 | "_EJ0", /* all others */ | ||
177 | ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ | ||
178 | |||
179 | IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ | ||
180 | "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ | ||
181 | ); /* all others */ | ||
182 | |||
183 | IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ | ||
184 | "_EJ0", /* 770x */ | ||
185 | ); /* all others */ | ||
186 | |||
187 | /* don't list other alternatives as we install a notify handler on the 570 */ | ||
188 | IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ | ||
189 | |||
190 | IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ | ||
191 | "^HKEY", /* R30, R31 */ | ||
192 | "HKEY", /* all others */ | ||
193 | ); /* 570 */ | ||
194 | |||
195 | IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ | ||
196 | IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ | ||
197 | |||
198 | IBM_HANDLE(led, ec, "SLED", /* 570 */ | ||
199 | "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ | ||
200 | "LED", /* all others */ | ||
201 | ); /* R30, R31 */ | ||
202 | |||
203 | IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ | ||
204 | IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ | ||
205 | IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ | ||
206 | IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */ | ||
207 | |||
208 | IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ | ||
209 | "\\FSPD", /* 600e/x, 770e, 770x */ | ||
210 | ); /* all others */ | ||
211 | |||
212 | IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ | ||
213 | "JFNS", /* 770x-JL */ | ||
214 | ); /* all others */ | ||
215 | |||
216 | #define IBM_HKEY_HID "IBM0068" | ||
217 | #define IBM_PCI_HID "PNP0A03" | ||
122 | 218 | ||
123 | struct ibm_struct { | 219 | struct ibm_struct { |
124 | char *name; | 220 | char *name; |
221 | char param[32]; | ||
125 | 222 | ||
126 | char *hid; | 223 | char *hid; |
127 | struct acpi_driver *driver; | 224 | struct acpi_driver *driver; |
128 | |||
129 | int (*init) (struct ibm_struct *); | ||
130 | int (*read) (struct ibm_struct *, char *); | ||
131 | int (*write) (struct ibm_struct *, char *); | ||
132 | void (*exit) (struct ibm_struct *); | ||
133 | 225 | ||
134 | void (*notify) (struct ibm_struct *, u32); | 226 | int (*init) (void); |
227 | int (*read) (char *); | ||
228 | int (*write) (char *); | ||
229 | void (*exit) (void); | ||
230 | |||
231 | void (*notify) (struct ibm_struct *, u32); | ||
135 | acpi_handle *handle; | 232 | acpi_handle *handle; |
136 | int type; | 233 | int type; |
137 | struct acpi_device *device; | 234 | struct acpi_device *device; |
@@ -141,17 +238,6 @@ struct ibm_struct { | |||
141 | int init_called; | 238 | int init_called; |
142 | int notify_installed; | 239 | int notify_installed; |
143 | 240 | ||
144 | int supported; | ||
145 | union { | ||
146 | struct { | ||
147 | int status; | ||
148 | int mask; | ||
149 | } hotkey; | ||
150 | struct { | ||
151 | int autoswitch; | ||
152 | } video; | ||
153 | } state; | ||
154 | |||
155 | int experimental; | 241 | int experimental; |
156 | }; | 242 | }; |
157 | 243 | ||
@@ -165,15 +251,15 @@ static int acpi_evalf(acpi_handle handle, | |||
165 | void *res, char *method, char *fmt, ...) | 251 | void *res, char *method, char *fmt, ...) |
166 | { | 252 | { |
167 | char *fmt0 = fmt; | 253 | char *fmt0 = fmt; |
168 | struct acpi_object_list params; | 254 | struct acpi_object_list params; |
169 | union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; | 255 | union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; |
170 | struct acpi_buffer result; | 256 | struct acpi_buffer result, *resultp; |
171 | union acpi_object out_obj; | 257 | union acpi_object out_obj; |
172 | acpi_status status; | 258 | acpi_status status; |
173 | va_list ap; | 259 | va_list ap; |
174 | char res_type; | 260 | char res_type; |
175 | int success; | 261 | int success; |
176 | int quiet; | 262 | int quiet; |
177 | 263 | ||
178 | if (!*fmt) { | 264 | if (!*fmt) { |
179 | printk(IBM_ERR "acpi_evalf() called with empty format\n"); | 265 | printk(IBM_ERR "acpi_evalf() called with empty format\n"); |
@@ -199,7 +285,7 @@ static int acpi_evalf(acpi_handle handle, | |||
199 | in_objs[params.count].integer.value = va_arg(ap, int); | 285 | in_objs[params.count].integer.value = va_arg(ap, int); |
200 | in_objs[params.count++].type = ACPI_TYPE_INTEGER; | 286 | in_objs[params.count++].type = ACPI_TYPE_INTEGER; |
201 | break; | 287 | break; |
202 | /* add more types as needed */ | 288 | /* add more types as needed */ |
203 | default: | 289 | default: |
204 | printk(IBM_ERR "acpi_evalf() called " | 290 | printk(IBM_ERR "acpi_evalf() called " |
205 | "with invalid format character '%c'\n", c); | 291 | "with invalid format character '%c'\n", c); |
@@ -208,21 +294,25 @@ static int acpi_evalf(acpi_handle handle, | |||
208 | } | 294 | } |
209 | va_end(ap); | 295 | va_end(ap); |
210 | 296 | ||
211 | result.length = sizeof(out_obj); | 297 | if (res_type != 'v') { |
212 | result.pointer = &out_obj; | 298 | result.length = sizeof(out_obj); |
299 | result.pointer = &out_obj; | ||
300 | resultp = &result; | ||
301 | } else | ||
302 | resultp = NULL; | ||
213 | 303 | ||
214 | status = acpi_evaluate_object(handle, method, ¶ms, &result); | 304 | status = acpi_evaluate_object(handle, method, ¶ms, resultp); |
215 | 305 | ||
216 | switch (res_type) { | 306 | switch (res_type) { |
217 | case 'd': /* int */ | 307 | case 'd': /* int */ |
218 | if (res) | 308 | if (res) |
219 | *(int *)res = out_obj.integer.value; | 309 | *(int *)res = out_obj.integer.value; |
220 | success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; | 310 | success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; |
221 | break; | 311 | break; |
222 | case 'v': /* void */ | 312 | case 'v': /* void */ |
223 | success = status == AE_OK; | 313 | success = status == AE_OK; |
224 | break; | 314 | break; |
225 | /* add more types as needed */ | 315 | /* add more types as needed */ |
226 | default: | 316 | default: |
227 | printk(IBM_ERR "acpi_evalf() called " | 317 | printk(IBM_ERR "acpi_evalf() called " |
228 | "with invalid format character '%c'\n", res_type); | 318 | "with invalid format character '%c'\n", res_type); |
@@ -262,7 +352,7 @@ static char *next_cmd(char **cmds) | |||
262 | return start; | 352 | return start; |
263 | } | 353 | } |
264 | 354 | ||
265 | static int driver_init(struct ibm_struct *ibm) | 355 | static int driver_init(void) |
266 | { | 356 | { |
267 | printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); | 357 | printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); |
268 | printk(IBM_INFO "%s\n", IBM_URL); | 358 | printk(IBM_INFO "%s\n", IBM_URL); |
@@ -270,7 +360,7 @@ static int driver_init(struct ibm_struct *ibm) | |||
270 | return 0; | 360 | return 0; |
271 | } | 361 | } |
272 | 362 | ||
273 | static int driver_read(struct ibm_struct *ibm, char *p) | 363 | static int driver_read(char *p) |
274 | { | 364 | { |
275 | int len = 0; | 365 | int len = 0; |
276 | 366 | ||
@@ -280,67 +370,74 @@ static int driver_read(struct ibm_struct *ibm, char *p) | |||
280 | return len; | 370 | return len; |
281 | } | 371 | } |
282 | 372 | ||
283 | static int hotkey_get(struct ibm_struct *ibm, int *status, int *mask) | 373 | static int hotkey_supported; |
374 | static int hotkey_mask_supported; | ||
375 | static int hotkey_orig_status; | ||
376 | static int hotkey_orig_mask; | ||
377 | |||
378 | static int hotkey_get(int *status, int *mask) | ||
284 | { | 379 | { |
285 | if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) | 380 | if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) |
286 | return -EIO; | 381 | return 0; |
287 | if (ibm->supported) { | 382 | |
288 | if (!acpi_evalf(hkey_handle, mask, "DHKN", "qd")) | 383 | if (hotkey_mask_supported) |
289 | return -EIO; | 384 | if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) |
290 | } else { | 385 | return 0; |
291 | *mask = ibm->state.hotkey.mask; | 386 | |
292 | } | 387 | return 1; |
293 | return 0; | ||
294 | } | 388 | } |
295 | 389 | ||
296 | static int hotkey_set(struct ibm_struct *ibm, int status, int mask) | 390 | static int hotkey_set(int status, int mask) |
297 | { | 391 | { |
298 | int i; | 392 | int i; |
299 | 393 | ||
300 | if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) | 394 | if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) |
301 | return -EIO; | ||
302 | |||
303 | if (!ibm->supported) | ||
304 | return 0; | 395 | return 0; |
305 | 396 | ||
306 | for (i=0; i<32; i++) { | 397 | if (hotkey_mask_supported) |
307 | int bit = ((1 << i) & mask) != 0; | 398 | for (i = 0; i < 32; i++) { |
308 | if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i+1, bit)) | 399 | int bit = ((1 << i) & mask) != 0; |
309 | return -EIO; | 400 | if (!acpi_evalf(hkey_handle, |
310 | } | 401 | NULL, "MHKM", "vdd", i + 1, bit)) |
402 | return 0; | ||
403 | } | ||
311 | 404 | ||
312 | return 0; | 405 | return 1; |
313 | } | 406 | } |
314 | 407 | ||
315 | static int hotkey_init(struct ibm_struct *ibm) | 408 | static int hotkey_init(void) |
316 | { | 409 | { |
317 | int ret; | 410 | /* hotkey not supported on 570 */ |
411 | hotkey_supported = hkey_handle != NULL; | ||
318 | 412 | ||
319 | ibm->supported = 1; | 413 | if (hotkey_supported) { |
320 | ret = hotkey_get(ibm, | 414 | /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
321 | &ibm->state.hotkey.status, | 415 | A30, R30, R31, T20-22, X20-21, X22-24 */ |
322 | &ibm->state.hotkey.mask); | 416 | hotkey_mask_supported = |
323 | if (ret < 0) { | 417 | acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); |
324 | /* mask not supported on A21e, A22p, T20, T21, X20, X22, X24 */ | 418 | |
325 | ibm->supported = 0; | 419 | if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) |
326 | ret = hotkey_get(ibm, | 420 | return -ENODEV; |
327 | &ibm->state.hotkey.status, | ||
328 | &ibm->state.hotkey.mask); | ||
329 | } | 421 | } |
330 | 422 | ||
331 | return ret; | 423 | return 0; |
332 | } | 424 | } |
333 | 425 | ||
334 | static int hotkey_read(struct ibm_struct *ibm, char *p) | 426 | static int hotkey_read(char *p) |
335 | { | 427 | { |
336 | int status, mask; | 428 | int status, mask; |
337 | int len = 0; | 429 | int len = 0; |
338 | 430 | ||
339 | if (hotkey_get(ibm, &status, &mask) < 0) | 431 | if (!hotkey_supported) { |
432 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
433 | return len; | ||
434 | } | ||
435 | |||
436 | if (!hotkey_get(&status, &mask)) | ||
340 | return -EIO; | 437 | return -EIO; |
341 | 438 | ||
342 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); | 439 | len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); |
343 | if (ibm->supported) { | 440 | if (hotkey_mask_supported) { |
344 | len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); | 441 | len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); |
345 | len += sprintf(p + len, | 442 | len += sprintf(p + len, |
346 | "commands:\tenable, disable, reset, <mask>\n"); | 443 | "commands:\tenable, disable, reset, <mask>\n"); |
@@ -352,23 +449,26 @@ static int hotkey_read(struct ibm_struct *ibm, char *p) | |||
352 | return len; | 449 | return len; |
353 | } | 450 | } |
354 | 451 | ||
355 | static int hotkey_write(struct ibm_struct *ibm, char *buf) | 452 | static int hotkey_write(char *buf) |
356 | { | 453 | { |
357 | int status, mask; | 454 | int status, mask; |
358 | char *cmd; | 455 | char *cmd; |
359 | int do_cmd = 0; | 456 | int do_cmd = 0; |
360 | 457 | ||
361 | if (hotkey_get(ibm, &status, &mask) < 0) | 458 | if (!hotkey_supported) |
362 | return -ENODEV; | 459 | return -ENODEV; |
363 | 460 | ||
461 | if (!hotkey_get(&status, &mask)) | ||
462 | return -EIO; | ||
463 | |||
364 | while ((cmd = next_cmd(&buf))) { | 464 | while ((cmd = next_cmd(&buf))) { |
365 | if (strlencmp(cmd, "enable") == 0) { | 465 | if (strlencmp(cmd, "enable") == 0) { |
366 | status = 1; | 466 | status = 1; |
367 | } else if (strlencmp(cmd, "disable") == 0) { | 467 | } else if (strlencmp(cmd, "disable") == 0) { |
368 | status = 0; | 468 | status = 0; |
369 | } else if (strlencmp(cmd, "reset") == 0) { | 469 | } else if (strlencmp(cmd, "reset") == 0) { |
370 | status = ibm->state.hotkey.status; | 470 | status = hotkey_orig_status; |
371 | mask = ibm->state.hotkey.mask; | 471 | mask = hotkey_orig_mask; |
372 | } else if (sscanf(cmd, "0x%x", &mask) == 1) { | 472 | } else if (sscanf(cmd, "0x%x", &mask) == 1) { |
373 | /* mask set */ | 473 | /* mask set */ |
374 | } else if (sscanf(cmd, "%x", &mask) == 1) { | 474 | } else if (sscanf(cmd, "%x", &mask) == 1) { |
@@ -378,15 +478,16 @@ static int hotkey_write(struct ibm_struct *ibm, char *buf) | |||
378 | do_cmd = 1; | 478 | do_cmd = 1; |
379 | } | 479 | } |
380 | 480 | ||
381 | if (do_cmd && hotkey_set(ibm, status, mask) < 0) | 481 | if (do_cmd && !hotkey_set(status, mask)) |
382 | return -EIO; | 482 | return -EIO; |
383 | 483 | ||
384 | return 0; | 484 | return 0; |
385 | } | 485 | } |
386 | 486 | ||
387 | static void hotkey_exit(struct ibm_struct *ibm) | 487 | static void hotkey_exit(void) |
388 | { | 488 | { |
389 | hotkey_set(ibm, ibm->state.hotkey.status, ibm->state.hotkey.mask); | 489 | if (hotkey_supported) |
490 | hotkey_set(hotkey_orig_status, hotkey_orig_mask); | ||
390 | } | 491 | } |
391 | 492 | ||
392 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) | 493 | static void hotkey_notify(struct ibm_struct *ibm, u32 event) |
@@ -398,33 +499,38 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
398 | else { | 499 | else { |
399 | printk(IBM_ERR "unknown hotkey event %d\n", event); | 500 | printk(IBM_ERR "unknown hotkey event %d\n", event); |
400 | acpi_bus_generate_event(ibm->device, event, 0); | 501 | acpi_bus_generate_event(ibm->device, event, 0); |
401 | } | 502 | } |
402 | } | 503 | } |
403 | 504 | ||
404 | static int bluetooth_init(struct ibm_struct *ibm) | 505 | static int bluetooth_supported; |
506 | |||
507 | static int bluetooth_init(void) | ||
405 | { | 508 | { |
406 | /* bluetooth not supported on A21e, G40, T20, T21, X20 */ | 509 | /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
407 | ibm->supported = acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); | 510 | G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ |
511 | bluetooth_supported = hkey_handle && | ||
512 | acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); | ||
408 | 513 | ||
409 | return 0; | 514 | return 0; |
410 | } | 515 | } |
411 | 516 | ||
412 | static int bluetooth_status(struct ibm_struct *ibm) | 517 | static int bluetooth_status(void) |
413 | { | 518 | { |
414 | int status; | 519 | int status; |
415 | 520 | ||
416 | if (!ibm->supported || !acpi_evalf(hkey_handle, &status, "GBDC", "d")) | 521 | if (!bluetooth_supported || |
522 | !acpi_evalf(hkey_handle, &status, "GBDC", "d")) | ||
417 | status = 0; | 523 | status = 0; |
418 | 524 | ||
419 | return status; | 525 | return status; |
420 | } | 526 | } |
421 | 527 | ||
422 | static int bluetooth_read(struct ibm_struct *ibm, char *p) | 528 | static int bluetooth_read(char *p) |
423 | { | 529 | { |
424 | int len = 0; | 530 | int len = 0; |
425 | int status = bluetooth_status(ibm); | 531 | int status = bluetooth_status(); |
426 | 532 | ||
427 | if (!ibm->supported) | 533 | if (!bluetooth_supported) |
428 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 534 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
429 | else if (!(status & 1)) | 535 | else if (!(status & 1)) |
430 | len += sprintf(p + len, "status:\t\tnot installed\n"); | 536 | len += sprintf(p + len, "status:\t\tnot installed\n"); |
@@ -436,14 +542,14 @@ static int bluetooth_read(struct ibm_struct *ibm, char *p) | |||
436 | return len; | 542 | return len; |
437 | } | 543 | } |
438 | 544 | ||
439 | static int bluetooth_write(struct ibm_struct *ibm, char *buf) | 545 | static int bluetooth_write(char *buf) |
440 | { | 546 | { |
441 | int status = bluetooth_status(ibm); | 547 | int status = bluetooth_status(); |
442 | char *cmd; | 548 | char *cmd; |
443 | int do_cmd = 0; | 549 | int do_cmd = 0; |
444 | 550 | ||
445 | if (!ibm->supported) | 551 | if (!bluetooth_supported) |
446 | return -EINVAL; | 552 | return -ENODEV; |
447 | 553 | ||
448 | while ((cmd = next_cmd(&buf))) { | 554 | while ((cmd = next_cmd(&buf))) { |
449 | if (strlencmp(cmd, "enable") == 0) { | 555 | if (strlencmp(cmd, "enable") == 0) { |
@@ -456,64 +562,166 @@ static int bluetooth_write(struct ibm_struct *ibm, char *buf) | |||
456 | } | 562 | } |
457 | 563 | ||
458 | if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) | 564 | if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) |
459 | return -EIO; | 565 | return -EIO; |
460 | 566 | ||
461 | return 0; | 567 | return 0; |
462 | } | 568 | } |
463 | 569 | ||
464 | static int video_init(struct ibm_struct *ibm) | 570 | static int video_supported; |
571 | static int video_orig_autosw; | ||
572 | |||
573 | #define VIDEO_570 1 | ||
574 | #define VIDEO_770 2 | ||
575 | #define VIDEO_NEW 3 | ||
576 | |||
577 | static int video_init(void) | ||
465 | { | 578 | { |
466 | if (!acpi_evalf(vid_handle, | 579 | int ivga; |
467 | &ibm->state.video.autoswitch, "^VDEE", "d")) | 580 | |
468 | return -ENODEV; | 581 | if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) |
582 | /* G41, assume IVGA doesn't change */ | ||
583 | vid_handle = vid2_handle; | ||
584 | |||
585 | if (!vid_handle) | ||
586 | /* video switching not supported on R30, R31 */ | ||
587 | video_supported = 0; | ||
588 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) | ||
589 | /* 570 */ | ||
590 | video_supported = VIDEO_570; | ||
591 | else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) | ||
592 | /* 600e/x, 770e, 770x */ | ||
593 | video_supported = VIDEO_770; | ||
594 | else | ||
595 | /* all others */ | ||
596 | video_supported = VIDEO_NEW; | ||
469 | 597 | ||
470 | return 0; | 598 | return 0; |
471 | } | 599 | } |
472 | 600 | ||
473 | static int video_status(struct ibm_struct *ibm) | 601 | static int video_status(void) |
474 | { | 602 | { |
475 | int status = 0; | 603 | int status = 0; |
476 | int i; | 604 | int i; |
477 | 605 | ||
478 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); | 606 | if (video_supported == VIDEO_570) { |
479 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) | 607 | if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) |
480 | status |= 0x02 * i; | 608 | status = i & 3; |
609 | } else if (video_supported == VIDEO_770) { | ||
610 | if (acpi_evalf(NULL, &i, "\\VCDL", "d")) | ||
611 | status |= 0x01 * i; | ||
612 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) | ||
613 | status |= 0x02 * i; | ||
614 | } else if (video_supported == VIDEO_NEW) { | ||
615 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); | ||
616 | if (acpi_evalf(NULL, &i, "\\VCDC", "d")) | ||
617 | status |= 0x02 * i; | ||
618 | |||
619 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); | ||
620 | if (acpi_evalf(NULL, &i, "\\VCDL", "d")) | ||
621 | status |= 0x01 * i; | ||
622 | if (acpi_evalf(NULL, &i, "\\VCDD", "d")) | ||
623 | status |= 0x08 * i; | ||
624 | } | ||
625 | |||
626 | return status; | ||
627 | } | ||
481 | 628 | ||
482 | acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); | 629 | static int video_autosw(void) |
483 | if (acpi_evalf(NULL, &i, "\\VCDL", "d")) | 630 | { |
484 | status |= 0x01 * i; | 631 | int autosw = 0; |
485 | if (acpi_evalf(NULL, &i, "\\VCDD", "d")) | ||
486 | status |= 0x08 * i; | ||
487 | 632 | ||
488 | if (acpi_evalf(vid_handle, &i, "^VDEE", "d")) | 633 | if (video_supported == VIDEO_570) |
489 | status |= 0x10 * (i & 1); | 634 | acpi_evalf(vid_handle, &autosw, "SWIT", "d"); |
635 | else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW) | ||
636 | acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); | ||
490 | 637 | ||
491 | return status; | 638 | return autosw & 1; |
492 | } | 639 | } |
493 | 640 | ||
494 | static int video_read(struct ibm_struct *ibm, char *p) | 641 | static int video_read(char *p) |
495 | { | 642 | { |
496 | int status = video_status(ibm); | 643 | int status = video_status(); |
644 | int autosw = video_autosw(); | ||
497 | int len = 0; | 645 | int len = 0; |
498 | 646 | ||
647 | if (!video_supported) { | ||
648 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
649 | return len; | ||
650 | } | ||
651 | |||
652 | len += sprintf(p + len, "status:\t\tsupported\n"); | ||
499 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); | 653 | len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); |
500 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); | 654 | len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); |
501 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); | 655 | if (video_supported == VIDEO_NEW) |
502 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(status, 4)); | 656 | len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); |
503 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable, " | 657 | len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); |
504 | "crt_enable, crt_disable\n"); | 658 | len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); |
505 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable, " | 659 | len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); |
506 | "auto_enable, auto_disable\n"); | 660 | if (video_supported == VIDEO_NEW) |
661 | len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); | ||
662 | len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); | ||
507 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); | 663 | len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); |
508 | 664 | ||
509 | return len; | 665 | return len; |
510 | } | 666 | } |
511 | 667 | ||
512 | static int video_write(struct ibm_struct *ibm, char *buf) | 668 | static int video_switch(void) |
669 | { | ||
670 | int autosw = video_autosw(); | ||
671 | int ret; | ||
672 | |||
673 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | ||
674 | return -EIO; | ||
675 | ret = video_supported == VIDEO_570 ? | ||
676 | acpi_evalf(ec_handle, NULL, "_Q16", "v") : | ||
677 | acpi_evalf(vid_handle, NULL, "VSWT", "v"); | ||
678 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); | ||
679 | |||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | static int video_expand(void) | ||
684 | { | ||
685 | if (video_supported == VIDEO_570) | ||
686 | return acpi_evalf(ec_handle, NULL, "_Q17", "v"); | ||
687 | else if (video_supported == VIDEO_770) | ||
688 | return acpi_evalf(vid_handle, NULL, "VEXP", "v"); | ||
689 | else | ||
690 | return acpi_evalf(NULL, NULL, "\\VEXP", "v"); | ||
691 | } | ||
692 | |||
693 | static int video_switch2(int status) | ||
694 | { | ||
695 | int ret; | ||
696 | |||
697 | if (video_supported == VIDEO_570) { | ||
698 | ret = acpi_evalf(NULL, NULL, | ||
699 | "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); | ||
700 | } else if (video_supported == VIDEO_770) { | ||
701 | int autosw = video_autosw(); | ||
702 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | ||
703 | return -EIO; | ||
704 | |||
705 | ret = acpi_evalf(vid_handle, NULL, | ||
706 | "ASWT", "vdd", status * 0x100, 0); | ||
707 | |||
708 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); | ||
709 | } else { | ||
710 | ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && | ||
711 | acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); | ||
712 | } | ||
713 | |||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | static int video_write(char *buf) | ||
513 | { | 718 | { |
514 | char *cmd; | 719 | char *cmd; |
515 | int enable, disable, status; | 720 | int enable, disable, status; |
516 | 721 | ||
722 | if (!video_supported) | ||
723 | return -ENODEV; | ||
724 | |||
517 | enable = disable = 0; | 725 | enable = disable = 0; |
518 | 726 | ||
519 | while ((cmd = next_cmd(&buf))) { | 727 | while ((cmd = next_cmd(&buf))) { |
@@ -525,9 +733,11 @@ static int video_write(struct ibm_struct *ibm, char *buf) | |||
525 | enable |= 0x02; | 733 | enable |= 0x02; |
526 | } else if (strlencmp(cmd, "crt_disable") == 0) { | 734 | } else if (strlencmp(cmd, "crt_disable") == 0) { |
527 | disable |= 0x02; | 735 | disable |= 0x02; |
528 | } else if (strlencmp(cmd, "dvi_enable") == 0) { | 736 | } else if (video_supported == VIDEO_NEW && |
737 | strlencmp(cmd, "dvi_enable") == 0) { | ||
529 | enable |= 0x08; | 738 | enable |= 0x08; |
530 | } else if (strlencmp(cmd, "dvi_disable") == 0) { | 739 | } else if (video_supported == VIDEO_NEW && |
740 | strlencmp(cmd, "dvi_disable") == 0) { | ||
531 | disable |= 0x08; | 741 | disable |= 0x08; |
532 | } else if (strlencmp(cmd, "auto_enable") == 0) { | 742 | } else if (strlencmp(cmd, "auto_enable") == 0) { |
533 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | 743 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) |
@@ -536,71 +746,75 @@ static int video_write(struct ibm_struct *ibm, char *buf) | |||
536 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) | 746 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) |
537 | return -EIO; | 747 | return -EIO; |
538 | } else if (strlencmp(cmd, "video_switch") == 0) { | 748 | } else if (strlencmp(cmd, "video_switch") == 0) { |
539 | int autoswitch; | 749 | if (!video_switch()) |
540 | if (!acpi_evalf(vid_handle, &autoswitch, "^VDEE", "d")) | ||
541 | return -EIO; | ||
542 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) | ||
543 | return -EIO; | ||
544 | if (!acpi_evalf(vid_handle, NULL, "VSWT", "v")) | ||
545 | return -EIO; | ||
546 | if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", | ||
547 | autoswitch)) | ||
548 | return -EIO; | 750 | return -EIO; |
549 | } else if (strlencmp(cmd, "expand_toggle") == 0) { | 751 | } else if (strlencmp(cmd, "expand_toggle") == 0) { |
550 | if (!acpi_evalf(NULL, NULL, "\\VEXP", "v")) | 752 | if (!video_expand()) |
551 | return -EIO; | 753 | return -EIO; |
552 | } else | 754 | } else |
553 | return -EINVAL; | 755 | return -EINVAL; |
554 | } | 756 | } |
555 | 757 | ||
556 | if (enable || disable) { | 758 | if (enable || disable) { |
557 | status = (video_status(ibm) & 0x0f & ~disable) | enable; | 759 | status = (video_status() & 0x0f & ~disable) | enable; |
558 | if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80)) | 760 | if (!video_switch2(status)) |
559 | return -EIO; | ||
560 | if (!acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1)) | ||
561 | return -EIO; | 761 | return -EIO; |
562 | } | 762 | } |
563 | 763 | ||
564 | return 0; | 764 | return 0; |
565 | } | 765 | } |
566 | 766 | ||
567 | static void video_exit(struct ibm_struct *ibm) | 767 | static void video_exit(void) |
568 | { | 768 | { |
569 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", | 769 | acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); |
570 | ibm->state.video.autoswitch); | ||
571 | } | 770 | } |
572 | 771 | ||
573 | static int light_init(struct ibm_struct *ibm) | 772 | static int light_supported; |
773 | static int light_status_supported; | ||
774 | |||
775 | static int light_init(void) | ||
574 | { | 776 | { |
575 | /* kblt not supported on G40, R32, X20 */ | 777 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ |
576 | ibm->supported = acpi_evalf(ec_handle, NULL, "KBLT", "qv"); | 778 | light_supported = (cmos_handle || lght_handle) && !ledb_handle; |
779 | |||
780 | if (light_supported) | ||
781 | /* light status not supported on | ||
782 | 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ | ||
783 | light_status_supported = acpi_evalf(ec_handle, NULL, | ||
784 | "KBLT", "qv"); | ||
577 | 785 | ||
578 | return 0; | 786 | return 0; |
579 | } | 787 | } |
580 | 788 | ||
581 | static int light_read(struct ibm_struct *ibm, char *p) | 789 | static int light_read(char *p) |
582 | { | 790 | { |
583 | int len = 0; | 791 | int len = 0; |
584 | int status = 0; | 792 | int status = 0; |
585 | 793 | ||
586 | if (ibm->supported) { | 794 | if (!light_supported) { |
795 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
796 | } else if (!light_status_supported) { | ||
797 | len += sprintf(p + len, "status:\t\tunknown\n"); | ||
798 | len += sprintf(p + len, "commands:\ton, off\n"); | ||
799 | } else { | ||
587 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) | 800 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) |
588 | return -EIO; | 801 | return -EIO; |
589 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 802 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); |
590 | } else | 803 | len += sprintf(p + len, "commands:\ton, off\n"); |
591 | len += sprintf(p + len, "status:\t\tunknown\n"); | 804 | } |
592 | |||
593 | len += sprintf(p + len, "commands:\ton, off\n"); | ||
594 | 805 | ||
595 | return len; | 806 | return len; |
596 | } | 807 | } |
597 | 808 | ||
598 | static int light_write(struct ibm_struct *ibm, char *buf) | 809 | static int light_write(char *buf) |
599 | { | 810 | { |
600 | int cmos_cmd, lght_cmd; | 811 | int cmos_cmd, lght_cmd; |
601 | char *cmd; | 812 | char *cmd; |
602 | int success; | 813 | int success; |
603 | 814 | ||
815 | if (!light_supported) | ||
816 | return -ENODEV; | ||
817 | |||
604 | while ((cmd = next_cmd(&buf))) { | 818 | while ((cmd = next_cmd(&buf))) { |
605 | if (strlencmp(cmd, "on") == 0) { | 819 | if (strlencmp(cmd, "on") == 0) { |
606 | cmos_cmd = 0x0c; | 820 | cmos_cmd = 0x0c; |
@@ -610,10 +824,10 @@ static int light_write(struct ibm_struct *ibm, char *buf) | |||
610 | lght_cmd = 0; | 824 | lght_cmd = 0; |
611 | } else | 825 | } else |
612 | return -EINVAL; | 826 | return -EINVAL; |
613 | 827 | ||
614 | success = cmos_handle ? | 828 | success = cmos_handle ? |
615 | acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : | 829 | acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : |
616 | acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); | 830 | acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); |
617 | if (!success) | 831 | if (!success) |
618 | return -EIO; | 832 | return -EIO; |
619 | } | 833 | } |
@@ -633,7 +847,7 @@ static int _sta(acpi_handle handle) | |||
633 | 847 | ||
634 | #define dock_docked() (_sta(dock_handle) & 1) | 848 | #define dock_docked() (_sta(dock_handle) & 1) |
635 | 849 | ||
636 | static int dock_read(struct ibm_struct *ibm, char *p) | 850 | static int dock_read(char *p) |
637 | { | 851 | { |
638 | int len = 0; | 852 | int len = 0; |
639 | int docked = dock_docked(); | 853 | int docked = dock_docked(); |
@@ -650,18 +864,17 @@ static int dock_read(struct ibm_struct *ibm, char *p) | |||
650 | return len; | 864 | return len; |
651 | } | 865 | } |
652 | 866 | ||
653 | static int dock_write(struct ibm_struct *ibm, char *buf) | 867 | static int dock_write(char *buf) |
654 | { | 868 | { |
655 | char *cmd; | 869 | char *cmd; |
656 | 870 | ||
657 | if (!dock_docked()) | 871 | if (!dock_docked()) |
658 | return -EINVAL; | 872 | return -ENODEV; |
659 | 873 | ||
660 | while ((cmd = next_cmd(&buf))) { | 874 | while ((cmd = next_cmd(&buf))) { |
661 | if (strlencmp(cmd, "undock") == 0) { | 875 | if (strlencmp(cmd, "undock") == 0) { |
662 | if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0)) | 876 | if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || |
663 | return -EIO; | 877 | !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) |
664 | if (!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) | ||
665 | return -EIO; | 878 | return -EIO; |
666 | } else if (strlencmp(cmd, "dock") == 0) { | 879 | } else if (strlencmp(cmd, "dock") == 0) { |
667 | if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) | 880 | if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) |
@@ -671,90 +884,131 @@ static int dock_write(struct ibm_struct *ibm, char *buf) | |||
671 | } | 884 | } |
672 | 885 | ||
673 | return 0; | 886 | return 0; |
674 | } | 887 | } |
675 | 888 | ||
676 | static void dock_notify(struct ibm_struct *ibm, u32 event) | 889 | static void dock_notify(struct ibm_struct *ibm, u32 event) |
677 | { | 890 | { |
678 | int docked = dock_docked(); | 891 | int docked = dock_docked(); |
679 | 892 | int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); | |
680 | if (event == 3 && docked) | 893 | |
681 | acpi_bus_generate_event(ibm->device, event, 1); /* button */ | 894 | if (event == 1 && !pci) /* 570 */ |
895 | acpi_bus_generate_event(ibm->device, event, 1); /* button */ | ||
896 | else if (event == 1 && pci) /* 570 */ | ||
897 | acpi_bus_generate_event(ibm->device, event, 3); /* dock */ | ||
898 | else if (event == 3 && docked) | ||
899 | acpi_bus_generate_event(ibm->device, event, 1); /* button */ | ||
682 | else if (event == 3 && !docked) | 900 | else if (event == 3 && !docked) |
683 | acpi_bus_generate_event(ibm->device, event, 2); /* undock */ | 901 | acpi_bus_generate_event(ibm->device, event, 2); /* undock */ |
684 | else if (event == 0 && docked) | 902 | else if (event == 0 && docked) |
685 | acpi_bus_generate_event(ibm->device, event, 3); /* dock */ | 903 | acpi_bus_generate_event(ibm->device, event, 3); /* dock */ |
686 | else { | 904 | else { |
687 | printk(IBM_ERR "unknown dock event %d, status %d\n", | 905 | printk(IBM_ERR "unknown dock event %d, status %d\n", |
688 | event, _sta(dock_handle)); | 906 | event, _sta(dock_handle)); |
689 | acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ | 907 | acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ |
690 | } | 908 | } |
691 | } | 909 | } |
692 | 910 | ||
693 | #define bay_occupied() (_sta(bay_handle) & 1) | 911 | static int bay_status_supported; |
912 | static int bay_status2_supported; | ||
913 | static int bay_eject_supported; | ||
914 | static int bay_eject2_supported; | ||
694 | 915 | ||
695 | static int bay_init(struct ibm_struct *ibm) | 916 | static int bay_init(void) |
696 | { | 917 | { |
697 | /* bay not supported on A21e, A22p, A31, A31p, G40, R32, R40e */ | 918 | bay_status_supported = bay_handle && |
698 | ibm->supported = bay_handle && bayej_handle && | 919 | acpi_evalf(bay_handle, NULL, "_STA", "qv"); |
699 | acpi_evalf(bay_handle, NULL, "_STA", "qv"); | 920 | bay_status2_supported = bay2_handle && |
921 | acpi_evalf(bay2_handle, NULL, "_STA", "qv"); | ||
922 | |||
923 | bay_eject_supported = bay_handle && bay_ej_handle && | ||
924 | (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); | ||
925 | bay_eject2_supported = bay2_handle && bay2_ej_handle && | ||
926 | (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); | ||
700 | 927 | ||
701 | return 0; | 928 | return 0; |
702 | } | 929 | } |
703 | 930 | ||
704 | static int bay_read(struct ibm_struct *ibm, char *p) | 931 | #define bay_occupied(b) (_sta(b##_handle) & 1) |
932 | |||
933 | static int bay_read(char *p) | ||
705 | { | 934 | { |
706 | int len = 0; | 935 | int len = 0; |
707 | int occupied = bay_occupied(); | 936 | int occupied = bay_occupied(bay); |
708 | 937 | int occupied2 = bay_occupied(bay2); | |
709 | if (!ibm->supported) | 938 | int eject, eject2; |
710 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 939 | |
711 | else if (!occupied) | 940 | len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? |
712 | len += sprintf(p + len, "status:\t\tunoccupied\n"); | 941 | (occupied ? "occupied" : "unoccupied") : |
713 | else { | 942 | "not supported"); |
714 | len += sprintf(p + len, "status:\t\toccupied\n"); | 943 | if (bay_status2_supported) |
944 | len += sprintf(p + len, "status2:\t%s\n", occupied2 ? | ||
945 | "occupied" : "unoccupied"); | ||
946 | |||
947 | eject = bay_eject_supported && occupied; | ||
948 | eject2 = bay_eject2_supported && occupied2; | ||
949 | |||
950 | if (eject && eject2) | ||
951 | len += sprintf(p + len, "commands:\teject, eject2\n"); | ||
952 | else if (eject) | ||
715 | len += sprintf(p + len, "commands:\teject\n"); | 953 | len += sprintf(p + len, "commands:\teject\n"); |
716 | } | 954 | else if (eject2) |
955 | len += sprintf(p + len, "commands:\teject2\n"); | ||
717 | 956 | ||
718 | return len; | 957 | return len; |
719 | } | 958 | } |
720 | 959 | ||
721 | static int bay_write(struct ibm_struct *ibm, char *buf) | 960 | static int bay_write(char *buf) |
722 | { | 961 | { |
723 | char *cmd; | 962 | char *cmd; |
724 | 963 | ||
964 | if (!bay_eject_supported && !bay_eject2_supported) | ||
965 | return -ENODEV; | ||
966 | |||
725 | while ((cmd = next_cmd(&buf))) { | 967 | while ((cmd = next_cmd(&buf))) { |
726 | if (strlencmp(cmd, "eject") == 0) { | 968 | if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { |
727 | if (!ibm->supported || | 969 | if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) |
728 | !acpi_evalf(bay_handle, NULL, "_EJ0", "vd", 1)) | 970 | return -EIO; |
971 | } else if (bay_eject2_supported && | ||
972 | strlencmp(cmd, "eject2") == 0) { | ||
973 | if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) | ||
729 | return -EIO; | 974 | return -EIO; |
730 | } else | 975 | } else |
731 | return -EINVAL; | 976 | return -EINVAL; |
732 | } | 977 | } |
733 | 978 | ||
734 | return 0; | 979 | return 0; |
735 | } | 980 | } |
736 | 981 | ||
737 | static void bay_notify(struct ibm_struct *ibm, u32 event) | 982 | static void bay_notify(struct ibm_struct *ibm, u32 event) |
738 | { | 983 | { |
739 | acpi_bus_generate_event(ibm->device, event, 0); | 984 | acpi_bus_generate_event(ibm->device, event, 0); |
740 | } | 985 | } |
741 | 986 | ||
742 | static int cmos_read(struct ibm_struct *ibm, char *p) | 987 | static int cmos_read(char *p) |
743 | { | 988 | { |
744 | int len = 0; | 989 | int len = 0; |
745 | 990 | ||
746 | /* cmos not supported on A21e, A22p, T20, T21, X20 */ | 991 | /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, |
992 | R30, R31, T20-22, X20-21 */ | ||
747 | if (!cmos_handle) | 993 | if (!cmos_handle) |
748 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 994 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
749 | else { | 995 | else { |
750 | len += sprintf(p + len, "status:\t\tsupported\n"); | 996 | len += sprintf(p + len, "status:\t\tsupported\n"); |
751 | len += sprintf(p + len, "commands:\t<int>\n"); | 997 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); |
752 | } | 998 | } |
753 | 999 | ||
754 | return len; | 1000 | return len; |
755 | } | 1001 | } |
756 | 1002 | ||
757 | static int cmos_write(struct ibm_struct *ibm, char *buf) | 1003 | static int cmos_eval(int cmos_cmd) |
1004 | { | ||
1005 | if (cmos_handle) | ||
1006 | return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); | ||
1007 | else | ||
1008 | return 1; | ||
1009 | } | ||
1010 | |||
1011 | static int cmos_write(char *buf) | ||
758 | { | 1012 | { |
759 | char *cmd; | 1013 | char *cmd; |
760 | int cmos_cmd; | 1014 | int cmos_cmd; |
@@ -763,183 +1017,644 @@ static int cmos_write(struct ibm_struct *ibm, char *buf) | |||
763 | return -EINVAL; | 1017 | return -EINVAL; |
764 | 1018 | ||
765 | while ((cmd = next_cmd(&buf))) { | 1019 | while ((cmd = next_cmd(&buf))) { |
766 | if (sscanf(cmd, "%u", &cmos_cmd) == 1) { | 1020 | if (sscanf(cmd, "%u", &cmos_cmd) == 1 && |
1021 | cmos_cmd >= 0 && cmos_cmd <= 21) { | ||
767 | /* cmos_cmd set */ | 1022 | /* cmos_cmd set */ |
768 | } else | 1023 | } else |
769 | return -EINVAL; | 1024 | return -EINVAL; |
770 | 1025 | ||
771 | if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) | 1026 | if (!cmos_eval(cmos_cmd)) |
772 | return -EIO; | 1027 | return -EIO; |
773 | } | 1028 | } |
774 | 1029 | ||
775 | return 0; | 1030 | return 0; |
776 | } | 1031 | } |
777 | 1032 | ||
778 | static int led_read(struct ibm_struct *ibm, char *p) | 1033 | static int led_supported; |
1034 | |||
1035 | #define LED_570 1 | ||
1036 | #define LED_OLD 2 | ||
1037 | #define LED_NEW 3 | ||
1038 | |||
1039 | static int led_init(void) | ||
1040 | { | ||
1041 | if (!led_handle) | ||
1042 | /* led not supported on R30, R31 */ | ||
1043 | led_supported = 0; | ||
1044 | else if (strlencmp(led_path, "SLED") == 0) | ||
1045 | /* 570 */ | ||
1046 | led_supported = LED_570; | ||
1047 | else if (strlencmp(led_path, "SYSL") == 0) | ||
1048 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ | ||
1049 | led_supported = LED_OLD; | ||
1050 | else | ||
1051 | /* all others */ | ||
1052 | led_supported = LED_NEW; | ||
1053 | |||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | #define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) | ||
1058 | |||
1059 | static int led_read(char *p) | ||
779 | { | 1060 | { |
780 | int len = 0; | 1061 | int len = 0; |
781 | 1062 | ||
1063 | if (!led_supported) { | ||
1064 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
1065 | return len; | ||
1066 | } | ||
1067 | len += sprintf(p + len, "status:\t\tsupported\n"); | ||
1068 | |||
1069 | if (led_supported == LED_570) { | ||
1070 | /* 570 */ | ||
1071 | int i, status; | ||
1072 | for (i = 0; i < 8; i++) { | ||
1073 | if (!acpi_evalf(ec_handle, | ||
1074 | &status, "GLED", "dd", 1 << i)) | ||
1075 | return -EIO; | ||
1076 | len += sprintf(p + len, "%d:\t\t%s\n", | ||
1077 | i, led_status(status)); | ||
1078 | } | ||
1079 | } | ||
1080 | |||
782 | len += sprintf(p + len, "commands:\t" | 1081 | len += sprintf(p + len, "commands:\t" |
783 | "<int> on, <int> off, <int> blink\n"); | 1082 | "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); |
784 | 1083 | ||
785 | return len; | 1084 | return len; |
786 | } | 1085 | } |
787 | 1086 | ||
788 | static int led_write(struct ibm_struct *ibm, char *buf) | 1087 | /* off, on, blink */ |
1088 | static const int led_sled_arg1[] = { 0, 1, 3 }; | ||
1089 | static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
1090 | static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
1091 | static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
1092 | |||
1093 | #define EC_HLCL 0x0c | ||
1094 | #define EC_HLBL 0x0d | ||
1095 | #define EC_HLMS 0x0e | ||
1096 | |||
1097 | static int led_write(char *buf) | ||
789 | { | 1098 | { |
790 | char *cmd; | 1099 | char *cmd; |
791 | unsigned int led; | 1100 | int led, ind, ret; |
792 | int led_cmd, sysl_cmd, bled_a, bled_b; | 1101 | |
1102 | if (!led_supported) | ||
1103 | return -ENODEV; | ||
793 | 1104 | ||
794 | while ((cmd = next_cmd(&buf))) { | 1105 | while ((cmd = next_cmd(&buf))) { |
795 | if (sscanf(cmd, "%u", &led) != 1) | 1106 | if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) |
796 | return -EINVAL; | 1107 | return -EINVAL; |
797 | 1108 | ||
798 | if (strstr(cmd, "blink")) { | 1109 | if (strstr(cmd, "off")) { |
799 | led_cmd = 0xc0; | 1110 | ind = 0; |
800 | sysl_cmd = 2; | ||
801 | bled_a = 2; | ||
802 | bled_b = 1; | ||
803 | } else if (strstr(cmd, "on")) { | 1111 | } else if (strstr(cmd, "on")) { |
804 | led_cmd = 0x80; | 1112 | ind = 1; |
805 | sysl_cmd = 1; | 1113 | } else if (strstr(cmd, "blink")) { |
806 | bled_a = 2; | 1114 | ind = 2; |
807 | bled_b = 0; | ||
808 | } else if (strstr(cmd, "off")) { | ||
809 | led_cmd = sysl_cmd = bled_a = bled_b = 0; | ||
810 | } else | 1115 | } else |
811 | return -EINVAL; | 1116 | return -EINVAL; |
812 | 1117 | ||
813 | if (led_handle) { | 1118 | if (led_supported == LED_570) { |
1119 | /* 570 */ | ||
1120 | led = 1 << led; | ||
814 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | 1121 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", |
815 | led, led_cmd)) | 1122 | led, led_sled_arg1[ind])) |
816 | return -EIO; | 1123 | return -EIO; |
817 | } else if (led < 2) { | 1124 | } else if (led_supported == LED_OLD) { |
818 | if (acpi_evalf(sysl_handle, NULL, NULL, "vdd", | 1125 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ |
819 | led, sysl_cmd)) | 1126 | led = 1 << led; |
1127 | ret = ec_write(EC_HLMS, led); | ||
1128 | if (ret >= 0) | ||
1129 | ret = | ||
1130 | ec_write(EC_HLBL, led * led_exp_hlbl[ind]); | ||
1131 | if (ret >= 0) | ||
1132 | ret = | ||
1133 | ec_write(EC_HLCL, led * led_exp_hlcl[ind]); | ||
1134 | if (ret < 0) | ||
1135 | return ret; | ||
1136 | } else { | ||
1137 | /* all others */ | ||
1138 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
1139 | led, led_led_arg1[ind])) | ||
820 | return -EIO; | 1140 | return -EIO; |
821 | } else if (led == 2 && bled_handle) { | 1141 | } |
822 | if (acpi_evalf(bled_handle, NULL, NULL, "vdd", | 1142 | } |
823 | bled_a, bled_b)) | 1143 | |
1144 | return 0; | ||
1145 | } | ||
1146 | |||
1147 | static int beep_read(char *p) | ||
1148 | { | ||
1149 | int len = 0; | ||
1150 | |||
1151 | if (!beep_handle) | ||
1152 | len += sprintf(p + len, "status:\t\tnot supported\n"); | ||
1153 | else { | ||
1154 | len += sprintf(p + len, "status:\t\tsupported\n"); | ||
1155 | len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); | ||
1156 | } | ||
1157 | |||
1158 | return len; | ||
1159 | } | ||
1160 | |||
1161 | static int beep_write(char *buf) | ||
1162 | { | ||
1163 | char *cmd; | ||
1164 | int beep_cmd; | ||
1165 | |||
1166 | if (!beep_handle) | ||
1167 | return -ENODEV; | ||
1168 | |||
1169 | while ((cmd = next_cmd(&buf))) { | ||
1170 | if (sscanf(cmd, "%u", &beep_cmd) == 1 && | ||
1171 | beep_cmd >= 0 && beep_cmd <= 17) { | ||
1172 | /* beep_cmd set */ | ||
1173 | } else | ||
1174 | return -EINVAL; | ||
1175 | if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) | ||
1176 | return -EIO; | ||
1177 | } | ||
1178 | |||
1179 | return 0; | ||
1180 | } | ||
1181 | |||
1182 | static int acpi_ec_read(int i, u8 * p) | ||
1183 | { | ||
1184 | int v; | ||
1185 | |||
1186 | if (ecrd_handle) { | ||
1187 | if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) | ||
1188 | return 0; | ||
1189 | *p = v; | ||
1190 | } else { | ||
1191 | if (ec_read(i, p) < 0) | ||
1192 | return 0; | ||
1193 | } | ||
1194 | |||
1195 | return 1; | ||
1196 | } | ||
1197 | |||
1198 | static int acpi_ec_write(int i, u8 v) | ||
1199 | { | ||
1200 | if (ecwr_handle) { | ||
1201 | if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) | ||
1202 | return 0; | ||
1203 | } else { | ||
1204 | if (ec_write(i, v) < 0) | ||
1205 | return 0; | ||
1206 | } | ||
1207 | |||
1208 | return 1; | ||
1209 | } | ||
1210 | |||
1211 | static int thermal_tmp_supported; | ||
1212 | static int thermal_updt_supported; | ||
1213 | |||
1214 | static int thermal_init(void) | ||
1215 | { | ||
1216 | /* temperatures not supported on 570, G4x, R30, R31, R32 */ | ||
1217 | thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); | ||
1218 | |||
1219 | /* 600e/x, 770e, 770x */ | ||
1220 | thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv"); | ||
1221 | |||
1222 | return 0; | ||
1223 | } | ||
1224 | |||
1225 | static int thermal_read(char *p) | ||
1226 | { | ||
1227 | int len = 0; | ||
1228 | |||
1229 | if (!thermal_tmp_supported) | ||
1230 | len += sprintf(p + len, "temperatures:\tnot supported\n"); | ||
1231 | else { | ||
1232 | int i, t; | ||
1233 | char tmpi[] = "TMPi"; | ||
1234 | s8 tmp[8]; | ||
1235 | |||
1236 | if (thermal_updt_supported) | ||
1237 | if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) | ||
1238 | return -EIO; | ||
1239 | |||
1240 | for (i = 0; i < 8; i++) { | ||
1241 | tmpi[3] = '0' + i; | ||
1242 | if (!acpi_evalf(ec_handle, &t, tmpi, "d")) | ||
1243 | return -EIO; | ||
1244 | if (thermal_updt_supported) | ||
1245 | tmp[i] = (t - 2732 + 5) / 10; | ||
1246 | else | ||
1247 | tmp[i] = t; | ||
1248 | } | ||
1249 | |||
1250 | len += sprintf(p + len, | ||
1251 | "temperatures:\t%d %d %d %d %d %d %d %d\n", | ||
1252 | tmp[0], tmp[1], tmp[2], tmp[3], | ||
1253 | tmp[4], tmp[5], tmp[6], tmp[7]); | ||
1254 | } | ||
1255 | |||
1256 | return len; | ||
1257 | } | ||
1258 | |||
1259 | static u8 ecdump_regs[256]; | ||
1260 | |||
1261 | static int ecdump_read(char *p) | ||
1262 | { | ||
1263 | int len = 0; | ||
1264 | int i, j; | ||
1265 | u8 v; | ||
1266 | |||
1267 | len += sprintf(p + len, "EC " | ||
1268 | " +00 +01 +02 +03 +04 +05 +06 +07" | ||
1269 | " +08 +09 +0a +0b +0c +0d +0e +0f\n"); | ||
1270 | for (i = 0; i < 256; i += 16) { | ||
1271 | len += sprintf(p + len, "EC 0x%02x:", i); | ||
1272 | for (j = 0; j < 16; j++) { | ||
1273 | if (!acpi_ec_read(i + j, &v)) | ||
1274 | break; | ||
1275 | if (v != ecdump_regs[i + j]) | ||
1276 | len += sprintf(p + len, " *%02x", v); | ||
1277 | else | ||
1278 | len += sprintf(p + len, " %02x", v); | ||
1279 | ecdump_regs[i + j] = v; | ||
1280 | } | ||
1281 | len += sprintf(p + len, "\n"); | ||
1282 | if (j != 16) | ||
1283 | break; | ||
1284 | } | ||
1285 | |||
1286 | /* These are way too dangerous to advertise openly... */ | ||
1287 | #if 0 | ||
1288 | len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" | ||
1289 | " (<offset> is 00-ff, <value> is 00-ff)\n"); | ||
1290 | len += sprintf(p + len, "commands:\t0x<offset> <value> " | ||
1291 | " (<offset> is 00-ff, <value> is 0-255)\n"); | ||
1292 | #endif | ||
1293 | return len; | ||
1294 | } | ||
1295 | |||
1296 | static int ecdump_write(char *buf) | ||
1297 | { | ||
1298 | char *cmd; | ||
1299 | int i, v; | ||
1300 | |||
1301 | while ((cmd = next_cmd(&buf))) { | ||
1302 | if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { | ||
1303 | /* i and v set */ | ||
1304 | } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { | ||
1305 | /* i and v set */ | ||
1306 | } else | ||
1307 | return -EINVAL; | ||
1308 | if (i >= 0 && i < 256 && v >= 0 && v < 256) { | ||
1309 | if (!acpi_ec_write(i, v)) | ||
824 | return -EIO; | 1310 | return -EIO; |
825 | } else | 1311 | } else |
826 | return -EINVAL; | 1312 | return -EINVAL; |
827 | } | 1313 | } |
828 | 1314 | ||
829 | return 0; | 1315 | return 0; |
830 | } | 1316 | } |
831 | 1317 | ||
832 | static int beep_read(struct ibm_struct *ibm, char *p) | 1318 | static int brightness_offset = 0x31; |
1319 | |||
1320 | static int brightness_read(char *p) | ||
833 | { | 1321 | { |
834 | int len = 0; | 1322 | int len = 0; |
1323 | u8 level; | ||
835 | 1324 | ||
836 | len += sprintf(p + len, "commands:\t<int>\n"); | 1325 | if (!acpi_ec_read(brightness_offset, &level)) { |
1326 | len += sprintf(p + len, "level:\t\tunreadable\n"); | ||
1327 | } else { | ||
1328 | len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); | ||
1329 | len += sprintf(p + len, "commands:\tup, down\n"); | ||
1330 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
1331 | " (<level> is 0-7)\n"); | ||
1332 | } | ||
837 | 1333 | ||
838 | return len; | 1334 | return len; |
839 | } | 1335 | } |
840 | 1336 | ||
841 | static int beep_write(struct ibm_struct *ibm, char *buf) | 1337 | #define BRIGHTNESS_UP 4 |
1338 | #define BRIGHTNESS_DOWN 5 | ||
1339 | |||
1340 | static int brightness_write(char *buf) | ||
842 | { | 1341 | { |
1342 | int cmos_cmd, inc, i; | ||
1343 | u8 level; | ||
1344 | int new_level; | ||
843 | char *cmd; | 1345 | char *cmd; |
844 | int beep_cmd; | ||
845 | 1346 | ||
846 | while ((cmd = next_cmd(&buf))) { | 1347 | while ((cmd = next_cmd(&buf))) { |
847 | if (sscanf(cmd, "%u", &beep_cmd) == 1) { | 1348 | if (!acpi_ec_read(brightness_offset, &level)) |
848 | /* beep_cmd set */ | 1349 | return -EIO; |
1350 | level &= 7; | ||
1351 | |||
1352 | if (strlencmp(cmd, "up") == 0) { | ||
1353 | new_level = level == 7 ? 7 : level + 1; | ||
1354 | } else if (strlencmp(cmd, "down") == 0) { | ||
1355 | new_level = level == 0 ? 0 : level - 1; | ||
1356 | } else if (sscanf(cmd, "level %d", &new_level) == 1 && | ||
1357 | new_level >= 0 && new_level <= 7) { | ||
1358 | /* new_level set */ | ||
1359 | } else | ||
1360 | return -EINVAL; | ||
1361 | |||
1362 | cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; | ||
1363 | inc = new_level > level ? 1 : -1; | ||
1364 | for (i = level; i != new_level; i += inc) { | ||
1365 | if (!cmos_eval(cmos_cmd)) | ||
1366 | return -EIO; | ||
1367 | if (!acpi_ec_write(brightness_offset, i + inc)) | ||
1368 | return -EIO; | ||
1369 | } | ||
1370 | } | ||
1371 | |||
1372 | return 0; | ||
1373 | } | ||
1374 | |||
1375 | static int volume_offset = 0x30; | ||
1376 | |||
1377 | static int volume_read(char *p) | ||
1378 | { | ||
1379 | int len = 0; | ||
1380 | u8 level; | ||
1381 | |||
1382 | if (!acpi_ec_read(volume_offset, &level)) { | ||
1383 | len += sprintf(p + len, "level:\t\tunreadable\n"); | ||
1384 | } else { | ||
1385 | len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); | ||
1386 | len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); | ||
1387 | len += sprintf(p + len, "commands:\tup, down, mute\n"); | ||
1388 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
1389 | " (<level> is 0-15)\n"); | ||
1390 | } | ||
1391 | |||
1392 | return len; | ||
1393 | } | ||
1394 | |||
1395 | #define VOLUME_DOWN 0 | ||
1396 | #define VOLUME_UP 1 | ||
1397 | #define VOLUME_MUTE 2 | ||
1398 | |||
1399 | static int volume_write(char *buf) | ||
1400 | { | ||
1401 | int cmos_cmd, inc, i; | ||
1402 | u8 level, mute; | ||
1403 | int new_level, new_mute; | ||
1404 | char *cmd; | ||
1405 | |||
1406 | while ((cmd = next_cmd(&buf))) { | ||
1407 | if (!acpi_ec_read(volume_offset, &level)) | ||
1408 | return -EIO; | ||
1409 | new_mute = mute = level & 0x40; | ||
1410 | new_level = level = level & 0xf; | ||
1411 | |||
1412 | if (strlencmp(cmd, "up") == 0) { | ||
1413 | if (mute) | ||
1414 | new_mute = 0; | ||
1415 | else | ||
1416 | new_level = level == 15 ? 15 : level + 1; | ||
1417 | } else if (strlencmp(cmd, "down") == 0) { | ||
1418 | if (mute) | ||
1419 | new_mute = 0; | ||
1420 | else | ||
1421 | new_level = level == 0 ? 0 : level - 1; | ||
1422 | } else if (sscanf(cmd, "level %d", &new_level) == 1 && | ||
1423 | new_level >= 0 && new_level <= 15) { | ||
1424 | /* new_level set */ | ||
1425 | } else if (strlencmp(cmd, "mute") == 0) { | ||
1426 | new_mute = 0x40; | ||
849 | } else | 1427 | } else |
850 | return -EINVAL; | 1428 | return -EINVAL; |
851 | 1429 | ||
852 | if (!acpi_evalf(beep_handle, NULL, NULL, "vd", beep_cmd)) | 1430 | if (new_level != level) { /* mute doesn't change */ |
1431 | cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN; | ||
1432 | inc = new_level > level ? 1 : -1; | ||
1433 | |||
1434 | if (mute && (!cmos_eval(cmos_cmd) || | ||
1435 | !acpi_ec_write(volume_offset, level))) | ||
1436 | return -EIO; | ||
1437 | |||
1438 | for (i = level; i != new_level; i += inc) | ||
1439 | if (!cmos_eval(cmos_cmd) || | ||
1440 | !acpi_ec_write(volume_offset, i + inc)) | ||
1441 | return -EIO; | ||
1442 | |||
1443 | if (mute && (!cmos_eval(VOLUME_MUTE) || | ||
1444 | !acpi_ec_write(volume_offset, | ||
1445 | new_level + mute))) | ||
1446 | return -EIO; | ||
1447 | } | ||
1448 | |||
1449 | if (new_mute != mute) { /* level doesn't change */ | ||
1450 | cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP; | ||
1451 | |||
1452 | if (!cmos_eval(cmos_cmd) || | ||
1453 | !acpi_ec_write(volume_offset, level + new_mute)) | ||
1454 | return -EIO; | ||
1455 | } | ||
1456 | } | ||
1457 | |||
1458 | return 0; | ||
1459 | } | ||
1460 | |||
1461 | static int fan_status_offset = 0x2f; | ||
1462 | static int fan_rpm_offset = 0x84; | ||
1463 | |||
1464 | static int fan_read(char *p) | ||
1465 | { | ||
1466 | int len = 0; | ||
1467 | int s; | ||
1468 | u8 lo, hi, status; | ||
1469 | |||
1470 | if (gfan_handle) { | ||
1471 | /* 570, 600e/x, 770e, 770x */ | ||
1472 | if (!acpi_evalf(gfan_handle, &s, NULL, "d")) | ||
853 | return -EIO; | 1473 | return -EIO; |
1474 | |||
1475 | len += sprintf(p + len, "level:\t\t%d\n", s); | ||
1476 | } else { | ||
1477 | /* all except 570, 600e/x, 770e, 770x */ | ||
1478 | if (!acpi_ec_read(fan_status_offset, &status)) | ||
1479 | len += sprintf(p + len, "status:\t\tunreadable\n"); | ||
1480 | else | ||
1481 | len += sprintf(p + len, "status:\t\t%s\n", | ||
1482 | enabled(status, 7)); | ||
1483 | |||
1484 | if (!acpi_ec_read(fan_rpm_offset, &lo) || | ||
1485 | !acpi_ec_read(fan_rpm_offset + 1, &hi)) | ||
1486 | len += sprintf(p + len, "speed:\t\tunreadable\n"); | ||
1487 | else | ||
1488 | len += sprintf(p + len, "speed:\t\t%d\n", | ||
1489 | (hi << 8) + lo); | ||
1490 | } | ||
1491 | |||
1492 | if (sfan_handle) | ||
1493 | /* 570, 770x-JL */ | ||
1494 | len += sprintf(p + len, "commands:\tlevel <level>" | ||
1495 | " (<level> is 0-7)\n"); | ||
1496 | if (!gfan_handle) | ||
1497 | /* all except 570, 600e/x, 770e, 770x */ | ||
1498 | len += sprintf(p + len, "commands:\tenable, disable\n"); | ||
1499 | if (fans_handle) | ||
1500 | /* X31, X40 */ | ||
1501 | len += sprintf(p + len, "commands:\tspeed <speed>" | ||
1502 | " (<speed> is 0-65535)\n"); | ||
1503 | |||
1504 | return len; | ||
1505 | } | ||
1506 | |||
1507 | static int fan_write(char *buf) | ||
1508 | { | ||
1509 | char *cmd; | ||
1510 | int level, speed; | ||
1511 | |||
1512 | while ((cmd = next_cmd(&buf))) { | ||
1513 | if (sfan_handle && | ||
1514 | sscanf(cmd, "level %d", &level) == 1 && | ||
1515 | level >= 0 && level <= 7) { | ||
1516 | /* 570, 770x-JL */ | ||
1517 | if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) | ||
1518 | return -EIO; | ||
1519 | } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) { | ||
1520 | /* all except 570, 600e/x, 770e, 770x */ | ||
1521 | if (!acpi_ec_write(fan_status_offset, 0x80)) | ||
1522 | return -EIO; | ||
1523 | } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) { | ||
1524 | /* all except 570, 600e/x, 770e, 770x */ | ||
1525 | if (!acpi_ec_write(fan_status_offset, 0x00)) | ||
1526 | return -EIO; | ||
1527 | } else if (fans_handle && | ||
1528 | sscanf(cmd, "speed %d", &speed) == 1 && | ||
1529 | speed >= 0 && speed <= 65535) { | ||
1530 | /* X31, X40 */ | ||
1531 | if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", | ||
1532 | speed, speed, speed)) | ||
1533 | return -EIO; | ||
1534 | } else | ||
1535 | return -EINVAL; | ||
854 | } | 1536 | } |
855 | 1537 | ||
856 | return 0; | 1538 | return 0; |
857 | } | 1539 | } |
858 | 1540 | ||
859 | static struct ibm_struct ibms[] = { | 1541 | static struct ibm_struct ibms[] = { |
860 | { | 1542 | { |
861 | .name = "driver", | 1543 | .name = "driver", |
862 | .init = driver_init, | 1544 | .init = driver_init, |
863 | .read = driver_read, | 1545 | .read = driver_read, |
864 | }, | 1546 | }, |
1547 | { | ||
1548 | .name = "hotkey", | ||
1549 | .hid = IBM_HKEY_HID, | ||
1550 | .init = hotkey_init, | ||
1551 | .read = hotkey_read, | ||
1552 | .write = hotkey_write, | ||
1553 | .exit = hotkey_exit, | ||
1554 | .notify = hotkey_notify, | ||
1555 | .handle = &hkey_handle, | ||
1556 | .type = ACPI_DEVICE_NOTIFY, | ||
1557 | }, | ||
1558 | { | ||
1559 | .name = "bluetooth", | ||
1560 | .init = bluetooth_init, | ||
1561 | .read = bluetooth_read, | ||
1562 | .write = bluetooth_write, | ||
1563 | }, | ||
1564 | { | ||
1565 | .name = "video", | ||
1566 | .init = video_init, | ||
1567 | .read = video_read, | ||
1568 | .write = video_write, | ||
1569 | .exit = video_exit, | ||
1570 | }, | ||
865 | { | 1571 | { |
866 | .name = "hotkey", | 1572 | .name = "light", |
867 | .hid = "IBM0068", | 1573 | .init = light_init, |
868 | .init = hotkey_init, | 1574 | .read = light_read, |
869 | .read = hotkey_read, | 1575 | .write = light_write, |
870 | .write = hotkey_write, | 1576 | }, |
871 | .exit = hotkey_exit, | ||
872 | .notify = hotkey_notify, | ||
873 | .handle = &hkey_handle, | ||
874 | .type = ACPI_DEVICE_NOTIFY, | ||
875 | }, | ||
876 | { | 1577 | { |
877 | .name = "bluetooth", | 1578 | .name = "dock", |
878 | .init = bluetooth_init, | 1579 | .read = dock_read, |
879 | .read = bluetooth_read, | 1580 | .write = dock_write, |
880 | .write = bluetooth_write, | 1581 | .notify = dock_notify, |
881 | }, | 1582 | .handle = &dock_handle, |
1583 | .type = ACPI_SYSTEM_NOTIFY, | ||
1584 | }, | ||
882 | { | 1585 | { |
883 | .name = "video", | 1586 | .name = "dock", |
884 | .init = video_init, | 1587 | .hid = IBM_PCI_HID, |
885 | .read = video_read, | 1588 | .notify = dock_notify, |
886 | .write = video_write, | 1589 | .handle = &pci_handle, |
887 | .exit = video_exit, | 1590 | .type = ACPI_SYSTEM_NOTIFY, |
888 | }, | 1591 | }, |
889 | { | 1592 | { |
890 | .name = "light", | 1593 | .name = "bay", |
891 | .init = light_init, | 1594 | .init = bay_init, |
892 | .read = light_read, | 1595 | .read = bay_read, |
893 | .write = light_write, | 1596 | .write = bay_write, |
894 | }, | 1597 | .notify = bay_notify, |
1598 | .handle = &bay_handle, | ||
1599 | .type = ACPI_SYSTEM_NOTIFY, | ||
1600 | }, | ||
895 | { | 1601 | { |
896 | .name = "dock", | 1602 | .name = "cmos", |
897 | .read = dock_read, | 1603 | .read = cmos_read, |
898 | .write = dock_write, | 1604 | .write = cmos_write, |
899 | .notify = dock_notify, | 1605 | }, |
900 | .handle = &dock_handle, | ||
901 | .type = ACPI_SYSTEM_NOTIFY, | ||
902 | }, | ||
903 | { | 1606 | { |
904 | .name = "bay", | 1607 | .name = "led", |
905 | .init = bay_init, | 1608 | .init = led_init, |
906 | .read = bay_read, | 1609 | .read = led_read, |
907 | .write = bay_write, | 1610 | .write = led_write, |
908 | .notify = bay_notify, | 1611 | }, |
909 | .handle = &bay_handle, | ||
910 | .type = ACPI_SYSTEM_NOTIFY, | ||
911 | }, | ||
912 | { | 1612 | { |
913 | .name = "cmos", | 1613 | .name = "beep", |
914 | .read = cmos_read, | 1614 | .read = beep_read, |
915 | .write = cmos_write, | 1615 | .write = beep_write, |
916 | .experimental = 1, | 1616 | }, |
917 | }, | ||
918 | { | 1617 | { |
919 | .name = "led", | 1618 | .name = "thermal", |
920 | .read = led_read, | 1619 | .init = thermal_init, |
921 | .write = led_write, | 1620 | .read = thermal_read, |
922 | .experimental = 1, | 1621 | }, |
923 | }, | ||
924 | { | 1622 | { |
925 | .name = "beep", | 1623 | .name = "ecdump", |
926 | .read = beep_read, | 1624 | .read = ecdump_read, |
927 | .write = beep_write, | 1625 | .write = ecdump_write, |
928 | .experimental = 1, | 1626 | .experimental = 1, |
929 | }, | 1627 | }, |
1628 | { | ||
1629 | .name = "brightness", | ||
1630 | .read = brightness_read, | ||
1631 | .write = brightness_write, | ||
1632 | .experimental = 1, | ||
1633 | }, | ||
1634 | { | ||
1635 | .name = "volume", | ||
1636 | .read = volume_read, | ||
1637 | .write = volume_write, | ||
1638 | .experimental = 1, | ||
1639 | }, | ||
1640 | { | ||
1641 | .name = "fan", | ||
1642 | .read = fan_read, | ||
1643 | .write = fan_write, | ||
1644 | .experimental = 1, | ||
1645 | }, | ||
930 | }; | 1646 | }; |
931 | #define NUM_IBMS (sizeof(ibms)/sizeof(ibms[0])) | ||
932 | 1647 | ||
933 | static int dispatch_read(char *page, char **start, off_t off, int count, | 1648 | static int dispatch_read(char *page, char **start, off_t off, int count, |
934 | int *eof, void *data) | 1649 | int *eof, void *data) |
935 | { | 1650 | { |
936 | struct ibm_struct *ibm = (struct ibm_struct *)data; | 1651 | struct ibm_struct *ibm = (struct ibm_struct *)data; |
937 | int len; | 1652 | int len; |
938 | 1653 | ||
939 | if (!ibm || !ibm->read) | 1654 | if (!ibm || !ibm->read) |
940 | return -EINVAL; | 1655 | return -EINVAL; |
941 | 1656 | ||
942 | len = ibm->read(ibm, page); | 1657 | len = ibm->read(page); |
943 | if (len < 0) | 1658 | if (len < 0) |
944 | return len; | 1659 | return len; |
945 | 1660 | ||
@@ -955,7 +1670,7 @@ static int dispatch_read(char *page, char **start, off_t off, int count, | |||
955 | return len; | 1670 | return len; |
956 | } | 1671 | } |
957 | 1672 | ||
958 | static int dispatch_write(struct file *file, const char __user *userbuf, | 1673 | static int dispatch_write(struct file *file, const char __user * userbuf, |
959 | unsigned long count, void *data) | 1674 | unsigned long count, void *data) |
960 | { | 1675 | { |
961 | struct ibm_struct *ibm = (struct ibm_struct *)data; | 1676 | struct ibm_struct *ibm = (struct ibm_struct *)data; |
@@ -969,20 +1684,20 @@ static int dispatch_write(struct file *file, const char __user *userbuf, | |||
969 | if (!kernbuf) | 1684 | if (!kernbuf) |
970 | return -ENOMEM; | 1685 | return -ENOMEM; |
971 | 1686 | ||
972 | if (copy_from_user(kernbuf, userbuf, count)) { | 1687 | if (copy_from_user(kernbuf, userbuf, count)) { |
973 | kfree(kernbuf); | 1688 | kfree(kernbuf); |
974 | return -EFAULT; | 1689 | return -EFAULT; |
975 | } | 1690 | } |
976 | 1691 | ||
977 | kernbuf[count] = 0; | 1692 | kernbuf[count] = 0; |
978 | strcat(kernbuf, ","); | 1693 | strcat(kernbuf, ","); |
979 | ret = ibm->write(ibm, kernbuf); | 1694 | ret = ibm->write(kernbuf); |
980 | if (ret == 0) | 1695 | if (ret == 0) |
981 | ret = count; | 1696 | ret = count; |
982 | 1697 | ||
983 | kfree(kernbuf); | 1698 | kfree(kernbuf); |
984 | 1699 | ||
985 | return ret; | 1700 | return ret; |
986 | } | 1701 | } |
987 | 1702 | ||
988 | static void dispatch_notify(acpi_handle handle, u32 event, void *data) | 1703 | static void dispatch_notify(acpi_handle handle, u32 event, void *data) |
@@ -995,7 +1710,7 @@ static void dispatch_notify(acpi_handle handle, u32 event, void *data) | |||
995 | ibm->notify(ibm, event); | 1710 | ibm->notify(ibm, event); |
996 | } | 1711 | } |
997 | 1712 | ||
998 | static int setup_notify(struct ibm_struct *ibm) | 1713 | static int __init setup_notify(struct ibm_struct *ibm) |
999 | { | 1714 | { |
1000 | acpi_status status; | 1715 | acpi_status status; |
1001 | int ret; | 1716 | int ret; |
@@ -1020,17 +1735,15 @@ static int setup_notify(struct ibm_struct *ibm) | |||
1020 | return -ENODEV; | 1735 | return -ENODEV; |
1021 | } | 1736 | } |
1022 | 1737 | ||
1023 | ibm->notify_installed = 1; | ||
1024 | |||
1025 | return 0; | 1738 | return 0; |
1026 | } | 1739 | } |
1027 | 1740 | ||
1028 | static int ibmacpi_device_add(struct acpi_device *device) | 1741 | static int __init ibm_device_add(struct acpi_device *device) |
1029 | { | 1742 | { |
1030 | return 0; | 1743 | return 0; |
1031 | } | 1744 | } |
1032 | 1745 | ||
1033 | static int register_driver(struct ibm_struct *ibm) | 1746 | static int __init register_driver(struct ibm_struct *ibm) |
1034 | { | 1747 | { |
1035 | int ret; | 1748 | int ret; |
1036 | 1749 | ||
@@ -1043,7 +1756,7 @@ static int register_driver(struct ibm_struct *ibm) | |||
1043 | memset(ibm->driver, 0, sizeof(struct acpi_driver)); | 1756 | memset(ibm->driver, 0, sizeof(struct acpi_driver)); |
1044 | sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); | 1757 | sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); |
1045 | ibm->driver->ids = ibm->hid; | 1758 | ibm->driver->ids = ibm->hid; |
1046 | ibm->driver->ops.add = &ibmacpi_device_add; | 1759 | ibm->driver->ops.add = &ibm_device_add; |
1047 | 1760 | ||
1048 | ret = acpi_bus_register_driver(ibm->driver); | 1761 | ret = acpi_bus_register_driver(ibm->driver); |
1049 | if (ret < 0) { | 1762 | if (ret < 0) { |
@@ -1055,7 +1768,7 @@ static int register_driver(struct ibm_struct *ibm) | |||
1055 | return ret; | 1768 | return ret; |
1056 | } | 1769 | } |
1057 | 1770 | ||
1058 | static int ibm_init(struct ibm_struct *ibm) | 1771 | static int __init ibm_init(struct ibm_struct *ibm) |
1059 | { | 1772 | { |
1060 | int ret; | 1773 | int ret; |
1061 | struct proc_dir_entry *entry; | 1774 | struct proc_dir_entry *entry; |
@@ -1071,31 +1784,34 @@ static int ibm_init(struct ibm_struct *ibm) | |||
1071 | } | 1784 | } |
1072 | 1785 | ||
1073 | if (ibm->init) { | 1786 | if (ibm->init) { |
1074 | ret = ibm->init(ibm); | 1787 | ret = ibm->init(); |
1075 | if (ret != 0) | 1788 | if (ret != 0) |
1076 | return ret; | 1789 | return ret; |
1077 | ibm->init_called = 1; | 1790 | ibm->init_called = 1; |
1078 | } | 1791 | } |
1079 | 1792 | ||
1080 | entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR, | 1793 | if (ibm->read) { |
1081 | proc_dir); | 1794 | entry = create_proc_entry(ibm->name, |
1082 | if (!entry) { | 1795 | S_IFREG | S_IRUGO | S_IWUSR, |
1083 | printk(IBM_ERR "unable to create proc entry %s\n", ibm->name); | 1796 | proc_dir); |
1084 | return -ENODEV; | 1797 | if (!entry) { |
1085 | } | 1798 | printk(IBM_ERR "unable to create proc entry %s\n", |
1086 | entry->owner = THIS_MODULE; | 1799 | ibm->name); |
1087 | ibm->proc_created = 1; | 1800 | return -ENODEV; |
1088 | 1801 | } | |
1089 | entry->data = ibm; | 1802 | entry->owner = THIS_MODULE; |
1090 | if (ibm->read) | 1803 | entry->data = ibm; |
1091 | entry->read_proc = &dispatch_read; | 1804 | entry->read_proc = &dispatch_read; |
1092 | if (ibm->write) | 1805 | if (ibm->write) |
1093 | entry->write_proc = &dispatch_write; | 1806 | entry->write_proc = &dispatch_write; |
1807 | ibm->proc_created = 1; | ||
1808 | } | ||
1094 | 1809 | ||
1095 | if (ibm->notify) { | 1810 | if (ibm->notify) { |
1096 | ret = setup_notify(ibm); | 1811 | ret = setup_notify(ibm); |
1097 | if (ret < 0) | 1812 | if (ret < 0) |
1098 | return ret; | 1813 | return ret; |
1814 | ibm->notify_installed = 1; | ||
1099 | } | 1815 | } |
1100 | 1816 | ||
1101 | return 0; | 1817 | return 0; |
@@ -1111,7 +1827,7 @@ static void ibm_exit(struct ibm_struct *ibm) | |||
1111 | remove_proc_entry(ibm->name, proc_dir); | 1827 | remove_proc_entry(ibm->name, proc_dir); |
1112 | 1828 | ||
1113 | if (ibm->init_called && ibm->exit) | 1829 | if (ibm->init_called && ibm->exit) |
1114 | ibm->exit(ibm); | 1830 | ibm->exit(); |
1115 | 1831 | ||
1116 | if (ibm->driver_registered) { | 1832 | if (ibm->driver_registered) { |
1117 | acpi_bus_unregister_driver(ibm->driver); | 1833 | acpi_bus_unregister_driver(ibm->driver); |
@@ -1119,60 +1835,66 @@ static void ibm_exit(struct ibm_struct *ibm) | |||
1119 | } | 1835 | } |
1120 | } | 1836 | } |
1121 | 1837 | ||
1122 | static int ibm_handle_init(char *name, | 1838 | static void __init ibm_handle_init(char *name, |
1123 | acpi_handle *handle, acpi_handle parent, | 1839 | acpi_handle * handle, acpi_handle parent, |
1124 | char **paths, int num_paths, int required) | 1840 | char **paths, int num_paths, char **path) |
1125 | { | 1841 | { |
1126 | int i; | 1842 | int i; |
1127 | acpi_status status; | 1843 | acpi_status status; |
1128 | 1844 | ||
1129 | for (i=0; i<num_paths; i++) { | 1845 | for (i = 0; i < num_paths; i++) { |
1130 | status = acpi_get_handle(parent, paths[i], handle); | 1846 | status = acpi_get_handle(parent, paths[i], handle); |
1131 | if (ACPI_SUCCESS(status)) | 1847 | if (ACPI_SUCCESS(status)) { |
1132 | return 0; | 1848 | *path = paths[i]; |
1133 | } | 1849 | return; |
1134 | 1850 | } | |
1135 | *handle = NULL; | ||
1136 | |||
1137 | if (required) { | ||
1138 | printk(IBM_ERR "%s object not found\n", name); | ||
1139 | return -1; | ||
1140 | } | 1851 | } |
1141 | 1852 | ||
1142 | return 0; | 1853 | *handle = NULL; |
1143 | } | 1854 | } |
1144 | 1855 | ||
1145 | #define IBM_HANDLE_INIT(object, required) \ | 1856 | #define IBM_HANDLE_INIT(object) \ |
1146 | ibm_handle_init(#object, &object##_handle, *object##_parent, \ | 1857 | ibm_handle_init(#object, &object##_handle, *object##_parent, \ |
1147 | object##_paths, sizeof(object##_paths)/sizeof(char*), required) | 1858 | object##_paths, ARRAY_SIZE(object##_paths), &object##_path) |
1148 | |||
1149 | 1859 | ||
1150 | static int set_ibm_param(const char *val, struct kernel_param *kp) | 1860 | static int set_ibm_param(const char *val, struct kernel_param *kp) |
1151 | { | 1861 | { |
1152 | unsigned int i; | 1862 | unsigned int i; |
1153 | char arg_with_comma[32]; | ||
1154 | |||
1155 | if (strlen(val) > 30) | ||
1156 | return -ENOSPC; | ||
1157 | 1863 | ||
1158 | strcpy(arg_with_comma, val); | 1864 | for (i = 0; i < ARRAY_SIZE(ibms); i++) |
1159 | strcat(arg_with_comma, ","); | 1865 | if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { |
1866 | if (strlen(val) > sizeof(ibms[i].param) - 2) | ||
1867 | return -ENOSPC; | ||
1868 | strcpy(ibms[i].param, val); | ||
1869 | strcat(ibms[i].param, ","); | ||
1870 | return 0; | ||
1871 | } | ||
1160 | 1872 | ||
1161 | for (i=0; i<NUM_IBMS; i++) | ||
1162 | if (strcmp(ibms[i].name, kp->name) == 0) | ||
1163 | return ibms[i].write(&ibms[i], arg_with_comma); | ||
1164 | BUG(); | ||
1165 | return -EINVAL; | 1873 | return -EINVAL; |
1166 | } | 1874 | } |
1167 | 1875 | ||
1168 | #define IBM_PARAM(feature) \ | 1876 | #define IBM_PARAM(feature) \ |
1169 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) | 1877 | module_param_call(feature, set_ibm_param, NULL, NULL, 0) |
1170 | 1878 | ||
1879 | IBM_PARAM(hotkey); | ||
1880 | IBM_PARAM(bluetooth); | ||
1881 | IBM_PARAM(video); | ||
1882 | IBM_PARAM(light); | ||
1883 | IBM_PARAM(dock); | ||
1884 | IBM_PARAM(bay); | ||
1885 | IBM_PARAM(cmos); | ||
1886 | IBM_PARAM(led); | ||
1887 | IBM_PARAM(beep); | ||
1888 | IBM_PARAM(ecdump); | ||
1889 | IBM_PARAM(brightness); | ||
1890 | IBM_PARAM(volume); | ||
1891 | IBM_PARAM(fan); | ||
1892 | |||
1171 | static void acpi_ibm_exit(void) | 1893 | static void acpi_ibm_exit(void) |
1172 | { | 1894 | { |
1173 | int i; | 1895 | int i; |
1174 | 1896 | ||
1175 | for (i=NUM_IBMS-1; i>=0; i--) | 1897 | for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) |
1176 | ibm_exit(&ibms[i]); | 1898 | ibm_exit(&ibms[i]); |
1177 | 1899 | ||
1178 | remove_proc_entry(IBM_DIR, acpi_root_dir); | 1900 | remove_proc_entry(IBM_DIR, acpi_root_dir); |
@@ -1185,30 +1907,40 @@ static int __init acpi_ibm_init(void) | |||
1185 | if (acpi_disabled) | 1907 | if (acpi_disabled) |
1186 | return -ENODEV; | 1908 | return -ENODEV; |
1187 | 1909 | ||
1188 | if (!acpi_specific_hotkey_enabled){ | 1910 | if (!acpi_specific_hotkey_enabled) { |
1189 | printk(IBM_ERR "Using generic hotkey driver\n"); | 1911 | printk(IBM_ERR "using generic hotkey driver\n"); |
1190 | return -ENODEV; | ||
1191 | } | ||
1192 | /* these handles are required */ | ||
1193 | if (IBM_HANDLE_INIT(ec, 1) < 0 || | ||
1194 | IBM_HANDLE_INIT(hkey, 1) < 0 || | ||
1195 | IBM_HANDLE_INIT(vid, 1) < 0 || | ||
1196 | IBM_HANDLE_INIT(beep, 1) < 0) | ||
1197 | return -ENODEV; | 1912 | return -ENODEV; |
1913 | } | ||
1198 | 1914 | ||
1199 | /* these handles have alternatives */ | 1915 | /* ec is required because many other handles are relative to it */ |
1200 | IBM_HANDLE_INIT(lght, 0); | 1916 | IBM_HANDLE_INIT(ec); |
1201 | if (IBM_HANDLE_INIT(cmos, !lght_handle) < 0) | 1917 | if (!ec_handle) { |
1202 | return -ENODEV; | 1918 | printk(IBM_ERR "ec object not found\n"); |
1203 | IBM_HANDLE_INIT(sysl, 0); | ||
1204 | if (IBM_HANDLE_INIT(led, !sysl_handle) < 0) | ||
1205 | return -ENODEV; | 1919 | return -ENODEV; |
1920 | } | ||
1206 | 1921 | ||
1207 | /* these handles are not required */ | 1922 | /* these handles are not required */ |
1208 | IBM_HANDLE_INIT(dock, 0); | 1923 | IBM_HANDLE_INIT(vid); |
1209 | IBM_HANDLE_INIT(bay, 0); | 1924 | IBM_HANDLE_INIT(vid2); |
1210 | IBM_HANDLE_INIT(bayej, 0); | 1925 | IBM_HANDLE_INIT(ledb); |
1211 | IBM_HANDLE_INIT(bled, 0); | 1926 | IBM_HANDLE_INIT(led); |
1927 | IBM_HANDLE_INIT(hkey); | ||
1928 | IBM_HANDLE_INIT(lght); | ||
1929 | IBM_HANDLE_INIT(cmos); | ||
1930 | IBM_HANDLE_INIT(dock); | ||
1931 | IBM_HANDLE_INIT(pci); | ||
1932 | IBM_HANDLE_INIT(bay); | ||
1933 | if (bay_handle) | ||
1934 | IBM_HANDLE_INIT(bay_ej); | ||
1935 | IBM_HANDLE_INIT(bay2); | ||
1936 | if (bay2_handle) | ||
1937 | IBM_HANDLE_INIT(bay2_ej); | ||
1938 | IBM_HANDLE_INIT(beep); | ||
1939 | IBM_HANDLE_INIT(ecrd); | ||
1940 | IBM_HANDLE_INIT(ecwr); | ||
1941 | IBM_HANDLE_INIT(fans); | ||
1942 | IBM_HANDLE_INIT(gfan); | ||
1943 | IBM_HANDLE_INIT(sfan); | ||
1212 | 1944 | ||
1213 | proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); | 1945 | proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); |
1214 | if (!proc_dir) { | 1946 | if (!proc_dir) { |
@@ -1216,9 +1948,11 @@ static int __init acpi_ibm_init(void) | |||
1216 | return -ENODEV; | 1948 | return -ENODEV; |
1217 | } | 1949 | } |
1218 | proc_dir->owner = THIS_MODULE; | 1950 | proc_dir->owner = THIS_MODULE; |
1219 | 1951 | ||
1220 | for (i=0; i<NUM_IBMS; i++) { | 1952 | for (i = 0; i < ARRAY_SIZE(ibms); i++) { |
1221 | ret = ibm_init(&ibms[i]); | 1953 | ret = ibm_init(&ibms[i]); |
1954 | if (ret >= 0 && *ibms[i].param) | ||
1955 | ret = ibms[i].write(ibms[i].param); | ||
1222 | if (ret < 0) { | 1956 | if (ret < 0) { |
1223 | acpi_ibm_exit(); | 1957 | acpi_ibm_exit(); |
1224 | return ret; | 1958 | return ret; |
@@ -1230,17 +1964,3 @@ static int __init acpi_ibm_init(void) | |||
1230 | 1964 | ||
1231 | module_init(acpi_ibm_init); | 1965 | module_init(acpi_ibm_init); |
1232 | module_exit(acpi_ibm_exit); | 1966 | module_exit(acpi_ibm_exit); |
1233 | |||
1234 | MODULE_AUTHOR("Borislav Deianov"); | ||
1235 | MODULE_DESCRIPTION(IBM_DESC); | ||
1236 | MODULE_LICENSE("GPL"); | ||
1237 | |||
1238 | IBM_PARAM(hotkey); | ||
1239 | IBM_PARAM(bluetooth); | ||
1240 | IBM_PARAM(video); | ||
1241 | IBM_PARAM(light); | ||
1242 | IBM_PARAM(dock); | ||
1243 | IBM_PARAM(bay); | ||
1244 | IBM_PARAM(cmos); | ||
1245 | IBM_PARAM(led); | ||
1246 | IBM_PARAM(beep); | ||