aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBorislav Deianov <borislav@users.sf.net>2005-08-17 00:00:00 -0400
committerLen Brown <len.brown@intel.com>2005-08-25 17:37:51 -0400
commit78f81cc4355c31c798564ff7efb253cc4cdce6c0 (patch)
treee3be9c36048b75db33218e49e7f75fea8be09261
parent9c2c38a122cc23d6a09b8004d60a33913683eedf (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.txt376
-rw-r--r--drivers/acpi/ibm_acpi.c1598
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
10This is a Linux ACPI driver for the IBM ThinkPad laptops. It aims to 10This is a Linux ACPI driver for the IBM ThinkPad laptops. It supports
11support various features of these laptops which are accessible through 11various features of these laptops which are accessible through the
12the ACPI framework but not otherwise supported by the generic Linux 12ACPI framework but not otherwise supported by the generic Linux ACPI
13ACPI drivers. 13drivers.
14 14
15 15
16Status 16Status
@@ -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
32A compatibility table by model and feature is maintained on the web 37A compatibility table by model and feature is maintained on the web
33site, http://ibm-acpi.sf.net/. I appreciate any success or failure 38site, 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
91commands supported by the various features is guaranteed to change 96commands supported by the various features is guaranteed to change
92frequently. 97frequently.
93 98
94Driver Version -- /proc/acpi/ibm/driver 99Driver version -- /proc/acpi/ibm/driver
95-------------------------------------- 100---------------------------------------
96 101
97The driver name and version. No commands can be written to this file. 102The driver name and version. No commands can be written to this file.
98 103
99Hot Keys -- /proc/acpi/ibm/hotkey 104Hot keys -- /proc/acpi/ibm/hotkey
100--------------------------------- 105---------------------------------
101 106
102Without this driver, only the Fn-F4 key (sleep button) generates an 107Without 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,
188the flickering or video corruption can be avoided. 193the flickering or video corruption can be avoided.
189 194
190The video_switch command cycles through the available video outputs 195The 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
193Video expansion can be toggled through this feature. This controls 198Video expansion can be toggled through this feature. This controls
194whether the display is expanded to fill the entire LCD screen when a 199whether 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
201features of this driver, as it uses the same ACPI methods as 206features of this driver, as it uses the same ACPI methods as
202Fn-F7. Video switching on the console should still work. 207Fn-F7. Video switching on the console should still work.
203 208
209UPDATE: There's now a patch for the X.org Radeon driver which
210addresses this issue. Some people are reporting success with the patch
211while others are still having problems. For more information:
212
213https://bugs.freedesktop.org/show_bug.cgi?id=2000
214
204ThinkLight control -- /proc/acpi/ibm/light 215ThinkLight 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
214Docking / Undocking -- /proc/acpi/ibm/dock 225Docking / undocking -- /proc/acpi/ibm/dock
215------------------------------------------ 226------------------------------------------
216 227
217Docking and undocking (e.g. with the X4 UltraBase) requires some 228Docking 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
228when originally booted. This is due to the current lack of support for 239when originally booted. This is due to the current lack of support for
229hot plugging of devices in the Linux ACPI framework. If the laptop was 240hot plugging of devices in the Linux ACPI framework. If the laptop was
230booted while not in the dock, the following message is shown in the 241booted while not in the dock, the following message is shown in the
231logs: "ibm_acpi: dock device not present". No dock-related events are 242logs:
232generated but the dock and undock commands described below still 243
233work. They can be executed manually or triggered by Fn key 244 Mar 17 01:42:34 aero kernel: ibm_acpi: dock device not present
234combinations (see the example acpid configuration files included in 245
235the driver tarball package available on the web site). 246In this case, no dock-related events are generated but the dock and
247undock commands described below still work. They can be executed
248manually or triggered by Fn key combinations (see the example acpid
249configuration files included in the driver tarball package available
250on the web site).
236 251
237When the eject request button on the dock is pressed, the first event 252When the eject request button on the dock is pressed, the first event
238above is generated. The handler for this event should issue the 253above is generated. The handler for this event should issue the
@@ -267,7 +282,7 @@ the only docking stations currently supported are the X-series
267UltraBase docks and "dumb" port replicators like the Mini Dock (the 282UltraBase docks and "dumb" port replicators like the Mini Dock (the
268latter don't need any ACPI support, actually). 283latter don't need any ACPI support, actually).
269 284
270UltraBay Eject -- /proc/acpi/ibm/bay 285UltraBay eject -- /proc/acpi/ibm/bay
271------------------------------------ 286------------------------------------
272 287
273Inserting or ejecting an UltraBay device requires some actions to be 288Inserting 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
284is in the dock, so it may not be present if the laptop was undocked). 299is in the dock, so it may not be present if the laptop was undocked).
285This is due to the current lack of support for hot plugging of devices 300This is due to the current lack of support for hot plugging of devices
286in the Linux ACPI framework. If the laptop was booted without the 301in the Linux ACPI framework. If the laptop was booted without the
287UltraBay, the following message is shown in the logs: "ibm_acpi: bay 302UltraBay, the following message is shown in the logs:
288device 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
306In this case, no bay-related events are generated but the eject
289command described below still works. It can be executed manually or 307command described below still works. It can be executed manually or
290triggered by a hot key combination. 308triggered by a hot key combination.
291 309
@@ -306,22 +324,33 @@ necessary to enable the UltraBay device (e.g. call idectl).
306The contents of the /proc/acpi/ibm/bay file shows the current status 324The contents of the /proc/acpi/ibm/bay file shows the current status
307of the UltraBay, as provided by the ACPI framework. 325of the UltraBay, as provided by the ACPI framework.
308 326
309Experimental Features 327EXPERIMENTAL warm eject support on the 600e/x, A22p and A3x (To use
310--------------------- 328this feature, you need to supply the experimental=1 parameter when
329loading the module):
330
331These models do not have a button near the UltraBay device to request
332a hot eject but rather require the laptop to be put to sleep
333(suspend-to-ram) before the bay device is ejected or inserted).
334The 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
342On the A3x, both the UltraBay 2000 and UltraBay Plus devices are
343supported. Use "eject2" instead of "eject" for the second bay.
311 344
312The following features are marked experimental because using them 345Note: the UltraBay eject support on the 600e/x, A22p and A3x is
313involves guessing the correct values of some parameters. Guessing 346EXPERIMENTAL and may not work as expected. USE WITH CAUTION!
314incorrectly may have undesirable effects like crashing your
315ThinkPad. USE THESE WITH CAUTION! To activate them, you'll need to
316supply the experimental=1 parameter when loading the module.
317 347
318Experimental: CMOS control - /proc/acpi/ibm/cmos 348CMOS control -- /proc/acpi/ibm/cmos
319------------------------------------------------ 349-----------------------------------
320 350
321This feature is used internally by the ACPI firmware to control the 351This feature is used internally by the ACPI firmware to control the
322ThinkLight on most newer ThinkPad models. It appears that it can also 352ThinkLight on most newer ThinkPad models. It may also control LCD
323control LCD brightness, sounds volume and more, but only on some 353brightness, sounds volume and more, but only on some models.
324models.
325 354
326The commands are non-negative integer numbers: 355The 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
333The range of numbers which are used internally by various models is 0 362The range of valid numbers is 0 to 21, but not all have an effect and
334to 21, but it's possible that numbers outside this range have 363the behavior varies from model to model. Here is the behavior on the
335interesting behavior. Here is the behavior on the X40 (tpb is the 364X40 (tpb is the ThinkPad Buttons utility):
336ThinkPad 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
349If you try this feature, please send me a report similar to the 377LED control -- /proc/acpi/ibm/led
350above. On models which allow control of LCD brightness or sound 378---------------------------------
351volume, I'd like to provide this functionality in an user-friendly
352way, but first I need a way to identify the models which this is
353possible.
354
355Experimental: LED control - /proc/acpi/ibm/LED
356----------------------------------------------
357 379
358Some of the LED indicators can be controlled through this feature. The 380Some of the LED indicators can be controlled through this feature. The
359available commands are: 381available 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
365The <led number> parameter is a non-negative integer. The range of LED 387The <led number> range is 0 to 7. The set of LEDs that can be
366numbers used internally by various models is 0 to 7 but it's possible 388controlled varies from model to model. Here is the mapping on the X40:
367that numbers outside this range are also valid. Here is the mapping on
368the 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
377All of the above can be turned on and off and can be made to blink. 397All of the above can be turned on and off and can be made to blink.
378 398
379If you try this feature, please send me a report similar to the 399ACPI sounds -- /proc/acpi/ibm/beep
380above. I'd like to provide this functionality in an user-friendly way, 400----------------------------------
381but first I need to identify the which numbers correspond to which
382LEDs on various models.
383
384Experimental: ACPI sounds - /proc/acpi/ibm/beep
385-----------------------------------------------
386 401
387The BEEP method is used internally by the ACPI firmware to provide 402The BEEP method is used internally by the ACPI firmware to provide
388audible alerts in various situtation. This feature allows the same 403audible alerts in various situations. This feature allows the same
389sounds to be triggered manually. 404sounds to be triggered manually.
390 405
391The commands are non-negative integer numbers: 406The 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
398The range of numbers which are used internally by various models is 0 410The valid <number> range is 0 to 17. Not all numbers trigger sounds
399to 17, but it's possible that numbers outside this range are also 411and the sounds vary from model to model. Here is the behavior on the
400valid. Here is the behavior on the X40: 412X40:
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
428Temperature sensors -- /proc/acpi/ibm/thermal
429---------------------------------------------
430
431Most ThinkPads include six or more separate temperature sensors but
432only expose the CPU temperature through the standard ACPI methods.
433This feature shows readings from up to eight different sensors. Some
434readings may not be valid, e.g. may show large negative values. For
435example, on the X40, a typical output may be:
436
437temperatures: 42 42 45 41 36 -128 33 -128
438
439Thomas Gruber took his R51 apart and traced all six active sensors in
440his laptop (the location of sensors may vary on other models):
441
4421: CPU
4432: Mini PCI Module
4443: HDD
4454: GPU
4465: Battery
4476: N/A
4487: Battery
4498: N/A
450
451No commands can be written to this file.
452
453EXPERIMENTAL: Embedded controller reigster dump -- /proc/acpi/ibm/ecdump
454------------------------------------------------------------------------
455
456This feature is marked EXPERIMENTAL because the implementation
457directly accesses hardware registers and may not work as expected. USE
458WITH CAUTION! To use this feature, you need to supply the
459experimental=1 parameter when loading the module.
460
461This feature dumps the values of 256 embedded controller
462registers. Values which have changed since the last time the registers
463were dumped are marked with a star:
464
465[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
466EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
467EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00
468EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00
469EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80
470EC 0x30: 01 07 1a 00 30 04 00 00 *85 00 00 10 00 50 00 00
471EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00
472EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 *bc *02 *bc
473EC 0x60: *02 *bc *02 00 00 00 00 00 00 00 00 00 00 00 00 00
474EC 0x70: 00 00 00 00 00 12 30 40 *24 *26 *2c *27 *20 80 *1f 80
475EC 0x80: 00 00 00 06 *37 *0e 03 00 00 00 0e 07 00 00 00 00
476EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
477EC 0xa0: *ff 09 ff 09 ff ff *64 00 *00 *00 *a2 41 *ff *ff *e0 00
478EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
479EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
480EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
481EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03
482EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a
483
484This feature can be used to determine the register holding the fan
485speed 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
491The first step makes sure various charging-related values don't
492vary. The second ensures that the fan-related values do vary, since
493the fan speed fluctuates a bit. The third will (hopefully) mark the
494fan register with a star:
495
496[root@x40 ibm-acpi]# cat /proc/acpi/ibm/ecdump
497EC +00 +01 +02 +03 +04 +05 +06 +07 +08 +09 +0a +0b +0c +0d +0e +0f
498EC 0x00: a7 47 87 01 fe 96 00 08 01 00 cb 00 00 00 40 00
499EC 0x10: 00 00 ff ff f4 3c 87 09 01 ff 42 01 ff ff 0d 00
500EC 0x20: 00 00 00 00 00 00 00 00 00 00 00 03 43 00 00 80
501EC 0x30: 01 07 1a 00 30 04 00 00 85 00 00 10 00 50 00 00
502EC 0x40: 00 00 00 00 00 00 14 01 00 04 00 00 00 00 00 00
503EC 0x50: 00 c0 02 0d 00 01 01 02 02 03 03 03 03 bc 02 bc
504EC 0x60: 02 bc 02 00 00 00 00 00 00 00 00 00 00 00 00 00
505EC 0x70: 00 00 00 00 00 12 30 40 24 27 2c 27 21 80 1f 80
506EC 0x80: 00 00 00 06 *be 0d 03 00 00 00 0e 07 00 00 00 00
507EC 0x90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
508EC 0xa0: ff 09 ff 09 ff ff 64 00 00 00 a2 41 ff ff e0 00
509EC 0xb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
510EC 0xc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
511EC 0xd0: 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
512EC 0xe0: 00 00 00 00 00 00 00 00 11 20 49 04 24 06 55 03
513EC 0xf0: 31 55 48 54 35 38 57 57 08 2f 45 73 07 65 6c 1a
514
515Another set of values that varies often is the temperature
516readings. Since temperatures don't change vary fast, you can take
517several quick dumps to eliminate them.
518
519You can use a similar method to figure out the meaning of other
520embedded controller registers - e.g. make sure nothing else changes
521except the charging or discharging battery to determine which
522registers contain the current battery capacity, etc. If you experiment
523with this, do send me your results (including some complete dumps with
524a description of the conditions when they were taken.)
525
526EXPERIMENTAL: LCD brightness control -- /proc/acpi/ibm/brightness
527-----------------------------------------------------------------
528
529This feature is marked EXPERIMENTAL because the implementation
530directly accesses hardware registers and may not work as expected. USE
531WITH CAUTION! To use this feature, you need to supply the
532experimental=1 parameter when loading the module.
533
534This feature allows software control of the LCD brightness on ThinkPad
535models which don't have a hardware brightness slider. The available
536commands 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
542The <level> number range is 0 to 7, although not all of them may be
543distinct. The current brightness level is shown in the file.
544
545EXPERIMENTAL: Volume control -- /proc/acpi/ibm/volume
546-----------------------------------------------------
547
548This feature is marked EXPERIMENTAL because the implementation
549directly accesses hardware registers and may not work as expected. USE
550WITH CAUTION! To use this feature, you need to supply the
551experimental=1 parameter when loading the module.
552
553This feature allows volume control on ThinkPad models which don't have
554a 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
561The <level> number range is 0 to 15 although not all of them may be
562distinct. The unmute the volume after the mute command, use either the
563up or down command (the level command will not unmute the volume).
564The current volume level and mute state is shown in the file.
565
566EXPERIMENTAL: fan speed, fan enable/disable -- /proc/acpi/ibm/fan
567-----------------------------------------------------------------
568
569This feature is marked EXPERIMENTAL because the implementation
570directly accesses hardware registers and may not work as expected. USE
571WITH CAUTION! To use this feature, you need to supply the
572experimental=1 parameter when loading the module.
573
574This feature attempts to show the current fan speed. The speed is read
575directly from the hardware registers of the embedded controller. This
576is known to work on later R, T and X series ThinkPads but may show a
577bogus value on other models.
578
579The 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
584WARNING WARNING WARNING: do not leave the fan disabled unless you are
585monitoring the temperature sensor readings and you are ready to enable
586it if necessary to avoid overheating.
587
588The fan only runs if it's enabled *and* the various temperature
589sensors which control it read high enough. On the X40, this seems to
590depend on the CPU and HDD temperatures. Specifically, the fan is
591turned on when either the CPU temperature climbs to 56 degrees or the
592HDD temperature climbs to 46 degrees. The fan is turned off when the
593CPU temperature drops to 49 degrees and the HDD temperature drops to
59441 degrees. These thresholds cannot currently be controlled.
595
596On the X31 and X40 (and ONLY on those models), the fan speed can be
597controlled to a certain degree. Once the fan is running, it can be
598forced to run faster or slower with the following command:
599
600 echo 'speed <speed>' > /proc/acpi/ibm/thermal
601
602The sustainable range of fan speeds on the X40 appears to be from
603about 3700 to about 7350. Values outside this range either do not have
604any effect or the fan speed eventually settles somewhere in that
605range. The fan cannot be stopped or started with this command.
606
607On the 570, temperature readings are not available through this
608feature and the fan control works a little differently. The fan speed
609is reported in levels from 0 (off) to 7 (max) and can be controlled
610with 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
414If you try this feature, please send me a report similar to the
415above. I'd like to provide this functionality in an user-friendly way,
416but first I need to identify the which numbers correspond to which
417sounds on various models.
418 613
419 614
420Multiple Command, Module Parameters 615Multiple Commands, Module Parameters
421----------------------------------- 616------------------------------------
422 617
423Multiple commands can be written to the proc files in one shot by 618Multiple commands can be written to the proc files in one shot by
424separating them with commas, for example: 619separating 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
455Toan T Nguyen <ntt@control.uchicago.edu> has written a SuSE powersave 652Toan T Nguyen <ntt@physics.ucla.edu> notes that Suse uses the
456script for the X20, included in config/usr/sbin/ibm_hotkeys_X20 653powersave program to suspend ('powersave --suspend-to-ram') or
654hibernate ('powersave --suspend-to-disk'). This means that the
655hibernate script is not needed on that distribution.
457 656
458Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event 657Henrik Brix Andersen <brix@gentoo.org> has written a Gentoo ACPI event
459handler script for the X31. You can get the latest version from 658handler script for the X31. You can get the latest version from
460http://dev.gentoo.org/~brix/files/x31.sh 659http://dev.gentoo.org/~brix/files/x31.sh
461 660
462David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh 661David Schweikert <dws@ee.eth.ch> has written an alternative blank.sh
463script which works on Debian systems, included in 662script which works on Debian systems. This scripts has now been
464configs/etc/acpi/actions/blank-debian.sh 663extended to also work on Fedora systems and included as the default
465 664blank.sh in the distribution.
466
467TODO
468----
469
470I'd like to implement the following features but haven't yet found the
471time 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
91MODULE_AUTHOR("Borislav Deianov");
92MODULE_DESCRIPTION(IBM_DESC);
93MODULE_VERSION(IBM_VERSION);
94MODULE_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
89IBM_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
94IBM_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 *
99IBM_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 *
105IBM_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
111IBM_HANDLE(bay, root, 141IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
112 "\\_SB.PCI0.IDE0.SCND.MSTR"); /* all except A21e */ 142 "\\_SB.PCI.ISA.EC", /* 570 */
113IBM_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 */
116IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A22p, T20, T21, X20 */ 146 "\\_SB.PCI0.ICH3.EC0", /* R31 */
117IBM_HANDLE(hkey, ec, "HKEY"); /* all */ 147 "\\_SB.PCI0.LPC.EC", /* all others */
118IBM_HANDLE(led, ec, "LED"); /* all except A21e, A22p, T20, T21, X20 */ 148 );
119IBM_HANDLE(sysl, ec, "SYSL"); /* A21e, A22p, T20, T21, X20 */ 149
120IBM_HANDLE(bled, ec, "BLED"); /* A22p, T20, T21, X20 */ 150IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
121IBM_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
157IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
158
159IBM_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
164IBM_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
170IBM_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
175IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
176 "_EJ0", /* all others */
177 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
178
179IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
180 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
181 ); /* all others */
182
183IBM_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 */
188IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
189
190IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
191 "^HKEY", /* R30, R31 */
192 "HKEY", /* all others */
193 ); /* 570 */
194
195IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
196IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
197
198IBM_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
203IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
204IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
205IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
206IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */
207
208IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
209 "\\FSPD", /* 600e/x, 770e, 770x */
210 ); /* all others */
211
212IBM_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
123struct ibm_struct { 219struct 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, &params, &result); 304 status = acpi_evaluate_object(handle, method, &params, 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
265static int driver_init(struct ibm_struct *ibm) 355static 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
273static int driver_read(struct ibm_struct *ibm, char *p) 363static 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
283static int hotkey_get(struct ibm_struct *ibm, int *status, int *mask) 373static int hotkey_supported;
374static int hotkey_mask_supported;
375static int hotkey_orig_status;
376static int hotkey_orig_mask;
377
378static 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
296static int hotkey_set(struct ibm_struct *ibm, int status, int mask) 390static 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
315static int hotkey_init(struct ibm_struct *ibm) 408static 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
334static int hotkey_read(struct ibm_struct *ibm, char *p) 426static 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
355static int hotkey_write(struct ibm_struct *ibm, char *buf) 452static 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
387static void hotkey_exit(struct ibm_struct *ibm) 487static 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
392static void hotkey_notify(struct ibm_struct *ibm, u32 event) 493static 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
404static int bluetooth_init(struct ibm_struct *ibm) 505static int bluetooth_supported;
506
507static 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
412static int bluetooth_status(struct ibm_struct *ibm) 517static 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
422static int bluetooth_read(struct ibm_struct *ibm, char *p) 528static 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
439static int bluetooth_write(struct ibm_struct *ibm, char *buf) 545static 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
464static int video_init(struct ibm_struct *ibm) 570static int video_supported;
571static int video_orig_autosw;
572
573#define VIDEO_570 1
574#define VIDEO_770 2
575#define VIDEO_NEW 3
576
577static 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
473static int video_status(struct ibm_struct *ibm) 601static 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); 629static 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
494static int video_read(struct ibm_struct *ibm, char *p) 641static 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
512static int video_write(struct ibm_struct *ibm, char *buf) 668static 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
683static 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
693static 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
717static 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
567static void video_exit(struct ibm_struct *ibm) 767static 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
573static int light_init(struct ibm_struct *ibm) 772static int light_supported;
773static int light_status_supported;
774
775static 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
581static int light_read(struct ibm_struct *ibm, char *p) 789static 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
598static int light_write(struct ibm_struct *ibm, char *buf) 809static 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
636static int dock_read(struct ibm_struct *ibm, char *p) 850static 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
653static int dock_write(struct ibm_struct *ibm, char *buf) 867static 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
676static void dock_notify(struct ibm_struct *ibm, u32 event) 889static 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) 911static int bay_status_supported;
912static int bay_status2_supported;
913static int bay_eject_supported;
914static int bay_eject2_supported;
694 915
695static int bay_init(struct ibm_struct *ibm) 916static 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
704static int bay_read(struct ibm_struct *ibm, char *p) 931#define bay_occupied(b) (_sta(b##_handle) & 1)
932
933static 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
721static int bay_write(struct ibm_struct *ibm, char *buf) 960static 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
737static void bay_notify(struct ibm_struct *ibm, u32 event) 982static 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
742static int cmos_read(struct ibm_struct *ibm, char *p) 987static 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
757static int cmos_write(struct ibm_struct *ibm, char *buf) 1003static 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
1011static 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
778static int led_read(struct ibm_struct *ibm, char *p) 1033static int led_supported;
1034
1035#define LED_570 1
1036#define LED_OLD 2
1037#define LED_NEW 3
1038
1039static 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
1059static 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
788static int led_write(struct ibm_struct *ibm, char *buf) 1087/* off, on, blink */
1088static const int led_sled_arg1[] = { 0, 1, 3 };
1089static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
1090static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
1091static 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
1097static 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
1147static 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
1161static 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
1182static 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
1198static 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
1211static int thermal_tmp_supported;
1212static int thermal_updt_supported;
1213
1214static 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
1225static 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
1259static u8 ecdump_regs[256];
1260
1261static 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
1296static 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
832static int beep_read(struct ibm_struct *ibm, char *p) 1318static int brightness_offset = 0x31;
1319
1320static 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
841static int beep_write(struct ibm_struct *ibm, char *buf) 1337#define BRIGHTNESS_UP 4
1338#define BRIGHTNESS_DOWN 5
1339
1340static 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
1375static int volume_offset = 0x30;
1376
1377static 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
1399static 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
1461static int fan_status_offset = 0x2f;
1462static int fan_rpm_offset = 0x84;
1463
1464static 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
1507static 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
859static struct ibm_struct ibms[] = { 1541static 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
933static int dispatch_read(char *page, char **start, off_t off, int count, 1648static 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
958static int dispatch_write(struct file *file, const char __user *userbuf, 1673static 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
988static void dispatch_notify(acpi_handle handle, u32 event, void *data) 1703static 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
998static int setup_notify(struct ibm_struct *ibm) 1713static 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
1028static int ibmacpi_device_add(struct acpi_device *device) 1741static int __init ibm_device_add(struct acpi_device *device)
1029{ 1742{
1030 return 0; 1743 return 0;
1031} 1744}
1032 1745
1033static int register_driver(struct ibm_struct *ibm) 1746static 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
1058static int ibm_init(struct ibm_struct *ibm) 1771static 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
1122static int ibm_handle_init(char *name, 1838static 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
1150static int set_ibm_param(const char *val, struct kernel_param *kp) 1860static 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
1879IBM_PARAM(hotkey);
1880IBM_PARAM(bluetooth);
1881IBM_PARAM(video);
1882IBM_PARAM(light);
1883IBM_PARAM(dock);
1884IBM_PARAM(bay);
1885IBM_PARAM(cmos);
1886IBM_PARAM(led);
1887IBM_PARAM(beep);
1888IBM_PARAM(ecdump);
1889IBM_PARAM(brightness);
1890IBM_PARAM(volume);
1891IBM_PARAM(fan);
1892
1171static void acpi_ibm_exit(void) 1893static 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
1231module_init(acpi_ibm_init); 1965module_init(acpi_ibm_init);
1232module_exit(acpi_ibm_exit); 1966module_exit(acpi_ibm_exit);
1233
1234MODULE_AUTHOR("Borislav Deianov");
1235MODULE_DESCRIPTION(IBM_DESC);
1236MODULE_LICENSE("GPL");
1237
1238IBM_PARAM(hotkey);
1239IBM_PARAM(bluetooth);
1240IBM_PARAM(video);
1241IBM_PARAM(light);
1242IBM_PARAM(dock);
1243IBM_PARAM(bay);
1244IBM_PARAM(cmos);
1245IBM_PARAM(led);
1246IBM_PARAM(beep);