aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ibm-acpi.txt376
-rw-r--r--drivers/acpi/ibm_acpi.c1358
2 files changed, 1327 insertions, 407 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 62233bd7147c..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,46 +113,120 @@ 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, "\\_SB.PCI0.ISA.EC", /* A21e, A22p, T20, T21, X20 */ 119/*
120 * The following models are supported to various degrees:
121 *
122 * 570, 600e, 600x, 770e, 770x
123 * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p
124 * G40, G41
125 * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51
126 * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43
127 * X20, X21, X22, X23, X24, X30, X31, X40
128 *
129 * The following models have no supported features:
130 *
131 * 240, 240x, i1400
132 *
133 * Still missing DSDTs for the following models:
134 *
135 * A20p, A22e, A22m
136 * R52
137 * S31
138 * T43p
139 */
140
141IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
142 "\\_SB.PCI.ISA.EC", /* 570 */
143 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
144 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
145 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
146 "\\_SB.PCI0.ICH3.EC0", /* R31 */
90 "\\_SB.PCI0.LPC.EC", /* all others */ 147 "\\_SB.PCI0.LPC.EC", /* all others */
91 ); 148 );
92 149
93IBM_HANDLE(vid, root, "\\_SB.PCI0.VID", /* A21e, G40, X30, X40 */ 150IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
151 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
152 "\\_SB.PCI0.VID0", /* 770e */
153 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
94 "\\_SB.PCI0.AGP.VID", /* all others */ 154 "\\_SB.PCI0.AGP.VID", /* all others */
95 ); 155 ); /* R30, R31 */
156
157IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
96 158
97IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50p, R51, T4x, X31, X40 */ 159IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
98 "\\CMOS", /* A3x, G40, R32, T23, T30, X22, X24, X30 */ 160 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
99 "\\CMS", /* R40, R40e */ 161 "\\CMS", /* R40, R40e */
100 ); /* A21e, A22p, T20, T21, X20 */ 162 ); /* all others */
101 163
102IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ 164IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
103 "\\_SB.PCI0.DOCK", /* A22p, T20, T21, X20 */ 165 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
104 "\\_SB.PCI0.PCI1.DOCK", /* all others */ 166 "\\_SB.PCI0.PCI1.DOCK", /* all others */
105 ); /* A21e, G40, R32, R40, R40e */ 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 */
106 189
107IBM_HANDLE(bay, root, "\\_SB.PCI0.IDE0.SCND.MSTR"); /* all except A21e */ 190IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
108IBM_HANDLE(bayej, root, "\\_SB.PCI0.IDE0.SCND.MSTR._EJ0"); /* all except A2x, A3x */ 191 "^HKEY", /* R30, R31 */
192 "HKEY", /* all others */
193 ); /* 570 */
109 194
110IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A22p, T20, T21, X20 */ 195IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
111IBM_HANDLE(hkey, ec, "HKEY"); /* all */ 196IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
112IBM_HANDLE(led, ec, "LED"); /* all except A21e, A22p, T20, T21, X20 */ 197
113IBM_HANDLE(sysl, ec, "SYSL"); /* A21e, A22p, T20, T21, X20 */ 198IBM_HANDLE(led, ec, "SLED", /* 570 */
114IBM_HANDLE(bled, ec, "BLED"); /* A22p, T20, T21, X20 */ 199 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
115IBM_HANDLE(beep, ec, "BEEP"); /* all models */ 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"
116 218
117struct ibm_struct { 219struct ibm_struct {
118 char *name; 220 char *name;
221 char param[32];
119 222
120 char *hid; 223 char *hid;
121 struct acpi_driver *driver; 224 struct acpi_driver *driver;
122 225
123 int (*init) (struct ibm_struct *); 226 int (*init) (void);
124 int (*read) (struct ibm_struct *, char *); 227 int (*read) (char *);
125 int (*write) (struct ibm_struct *, char *); 228 int (*write) (char *);
126 void (*exit) (struct ibm_struct *); 229 void (*exit) (void);
127 230
128 void (*notify) (struct ibm_struct *, u32); 231 void (*notify) (struct ibm_struct *, u32);
129 acpi_handle *handle; 232 acpi_handle *handle;
@@ -135,17 +238,6 @@ struct ibm_struct {
135 int init_called; 238 int init_called;
136 int notify_installed; 239 int notify_installed;
137 240
138 int supported;
139 union {
140 struct {
141 int status;
142 int mask;
143 } hotkey;
144 struct {
145 int autoswitch;
146 } video;
147 } state;
148
149 int experimental; 241 int experimental;
150}; 242};
151 243
@@ -161,7 +253,7 @@ static int acpi_evalf(acpi_handle handle,
161 char *fmt0 = fmt; 253 char *fmt0 = fmt;
162 struct acpi_object_list params; 254 struct acpi_object_list params;
163 union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; 255 union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
164 struct acpi_buffer result; 256 struct acpi_buffer result, *resultp;
165 union acpi_object out_obj; 257 union acpi_object out_obj;
166 acpi_status status; 258 acpi_status status;
167 va_list ap; 259 va_list ap;
@@ -202,10 +294,14 @@ static int acpi_evalf(acpi_handle handle,
202 } 294 }
203 va_end(ap); 295 va_end(ap);
204 296
205 result.length = sizeof(out_obj); 297 if (res_type != 'v') {
206 result.pointer = &out_obj; 298 result.length = sizeof(out_obj);
299 result.pointer = &out_obj;
300 resultp = &result;
301 } else
302 resultp = NULL;
207 303
208 status = acpi_evaluate_object(handle, method, &params, &result); 304 status = acpi_evaluate_object(handle, method, &params, resultp);
209 305
210 switch (res_type) { 306 switch (res_type) {
211 case 'd': /* int */ 307 case 'd': /* int */
@@ -256,7 +352,7 @@ static char *next_cmd(char **cmds)
256 return start; 352 return start;
257} 353}
258 354
259static int driver_init(struct ibm_struct *ibm) 355static int driver_init(void)
260{ 356{
261 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); 357 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
262 printk(IBM_INFO "%s\n", IBM_URL); 358 printk(IBM_INFO "%s\n", IBM_URL);
@@ -264,7 +360,7 @@ static int driver_init(struct ibm_struct *ibm)
264 return 0; 360 return 0;
265} 361}
266 362
267static int driver_read(struct ibm_struct *ibm, char *p) 363static int driver_read(char *p)
268{ 364{
269 int len = 0; 365 int len = 0;
270 366
@@ -274,66 +370,74 @@ static int driver_read(struct ibm_struct *ibm, char *p)
274 return len; 370 return len;
275} 371}
276 372
277static 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)
278{ 379{
279 if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) 380 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
280 return -EIO; 381 return 0;
281 if (ibm->supported) { 382
282 if (!acpi_evalf(hkey_handle, mask, "DHKN", "qd")) 383 if (hotkey_mask_supported)
283 return -EIO; 384 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
284 } else { 385 return 0;
285 *mask = ibm->state.hotkey.mask; 386
286 } 387 return 1;
287 return 0;
288} 388}
289 389
290static int hotkey_set(struct ibm_struct *ibm, int status, int mask) 390static int hotkey_set(int status, int mask)
291{ 391{
292 int i; 392 int i;
293 393
294 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) 394 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
295 return -EIO;
296
297 if (!ibm->supported)
298 return 0; 395 return 0;
299 396
300 for (i = 0; i < 32; i++) { 397 if (hotkey_mask_supported)
301 int bit = ((1 << i) & mask) != 0; 398 for (i = 0; i < 32; i++) {
302 if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i + 1, bit)) 399 int bit = ((1 << i) & mask) != 0;
303 return -EIO; 400 if (!acpi_evalf(hkey_handle,
304 } 401 NULL, "MHKM", "vdd", i + 1, bit))
402 return 0;
403 }
305 404
306 return 0; 405 return 1;
307} 406}
308 407
309static int hotkey_init(struct ibm_struct *ibm) 408static int hotkey_init(void)
310{ 409{
311 int ret; 410 /* hotkey not supported on 570 */
411 hotkey_supported = hkey_handle != NULL;
312 412
313 ibm->supported = 1; 413 if (hotkey_supported) {
314 ret = hotkey_get(ibm, 414 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
315 &ibm->state.hotkey.status, &ibm->state.hotkey.mask); 415 A30, R30, R31, T20-22, X20-21, X22-24 */
316 if (ret < 0) { 416 hotkey_mask_supported =
317 /* mask not supported on A21e, A22p, T20, T21, X20, X22, X24 */ 417 acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
318 ibm->supported = 0; 418
319 ret = hotkey_get(ibm, 419 if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
320 &ibm->state.hotkey.status, 420 return -ENODEV;
321 &ibm->state.hotkey.mask);
322 } 421 }
323 422
324 return ret; 423 return 0;
325} 424}
326 425
327static int hotkey_read(struct ibm_struct *ibm, char *p) 426static int hotkey_read(char *p)
328{ 427{
329 int status, mask; 428 int status, mask;
330 int len = 0; 429 int len = 0;
331 430
332 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))
333 return -EIO; 437 return -EIO;
334 438
335 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));
336 if (ibm->supported) { 440 if (hotkey_mask_supported) {
337 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); 441 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
338 len += sprintf(p + len, 442 len += sprintf(p + len,
339 "commands:\tenable, disable, reset, <mask>\n"); 443 "commands:\tenable, disable, reset, <mask>\n");
@@ -345,23 +449,26 @@ static int hotkey_read(struct ibm_struct *ibm, char *p)
345 return len; 449 return len;
346} 450}
347 451
348static int hotkey_write(struct ibm_struct *ibm, char *buf) 452static int hotkey_write(char *buf)
349{ 453{
350 int status, mask; 454 int status, mask;
351 char *cmd; 455 char *cmd;
352 int do_cmd = 0; 456 int do_cmd = 0;
353 457
354 if (hotkey_get(ibm, &status, &mask) < 0) 458 if (!hotkey_supported)
355 return -ENODEV; 459 return -ENODEV;
356 460
461 if (!hotkey_get(&status, &mask))
462 return -EIO;
463
357 while ((cmd = next_cmd(&buf))) { 464 while ((cmd = next_cmd(&buf))) {
358 if (strlencmp(cmd, "enable") == 0) { 465 if (strlencmp(cmd, "enable") == 0) {
359 status = 1; 466 status = 1;
360 } else if (strlencmp(cmd, "disable") == 0) { 467 } else if (strlencmp(cmd, "disable") == 0) {
361 status = 0; 468 status = 0;
362 } else if (strlencmp(cmd, "reset") == 0) { 469 } else if (strlencmp(cmd, "reset") == 0) {
363 status = ibm->state.hotkey.status; 470 status = hotkey_orig_status;
364 mask = ibm->state.hotkey.mask; 471 mask = hotkey_orig_mask;
365 } else if (sscanf(cmd, "0x%x", &mask) == 1) { 472 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
366 /* mask set */ 473 /* mask set */
367 } else if (sscanf(cmd, "%x", &mask) == 1) { 474 } else if (sscanf(cmd, "%x", &mask) == 1) {
@@ -371,15 +478,16 @@ static int hotkey_write(struct ibm_struct *ibm, char *buf)
371 do_cmd = 1; 478 do_cmd = 1;
372 } 479 }
373 480
374 if (do_cmd && hotkey_set(ibm, status, mask) < 0) 481 if (do_cmd && !hotkey_set(status, mask))
375 return -EIO; 482 return -EIO;
376 483
377 return 0; 484 return 0;
378} 485}
379 486
380static void hotkey_exit(struct ibm_struct *ibm) 487static void hotkey_exit(void)
381{ 488{
382 hotkey_set(ibm, ibm->state.hotkey.status, ibm->state.hotkey.mask); 489 if (hotkey_supported)
490 hotkey_set(hotkey_orig_status, hotkey_orig_mask);
383} 491}
384 492
385static void hotkey_notify(struct ibm_struct *ibm, u32 event) 493static void hotkey_notify(struct ibm_struct *ibm, u32 event)
@@ -394,30 +502,35 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
394 } 502 }
395} 503}
396 504
397static int bluetooth_init(struct ibm_struct *ibm) 505static int bluetooth_supported;
506
507static int bluetooth_init(void)
398{ 508{
399 /* bluetooth not supported on A21e, G40, T20, T21, X20 */ 509 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
400 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");
401 513
402 return 0; 514 return 0;
403} 515}
404 516
405static int bluetooth_status(struct ibm_struct *ibm) 517static int bluetooth_status(void)
406{ 518{
407 int status; 519 int status;
408 520
409 if (!ibm->supported || !acpi_evalf(hkey_handle, &status, "GBDC", "d")) 521 if (!bluetooth_supported ||
522 !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
410 status = 0; 523 status = 0;
411 524
412 return status; 525 return status;
413} 526}
414 527
415static int bluetooth_read(struct ibm_struct *ibm, char *p) 528static int bluetooth_read(char *p)
416{ 529{
417 int len = 0; 530 int len = 0;
418 int status = bluetooth_status(ibm); 531 int status = bluetooth_status();
419 532
420 if (!ibm->supported) 533 if (!bluetooth_supported)
421 len += sprintf(p + len, "status:\t\tnot supported\n"); 534 len += sprintf(p + len, "status:\t\tnot supported\n");
422 else if (!(status & 1)) 535 else if (!(status & 1))
423 len += sprintf(p + len, "status:\t\tnot installed\n"); 536 len += sprintf(p + len, "status:\t\tnot installed\n");
@@ -429,14 +542,14 @@ static int bluetooth_read(struct ibm_struct *ibm, char *p)
429 return len; 542 return len;
430} 543}
431 544
432static int bluetooth_write(struct ibm_struct *ibm, char *buf) 545static int bluetooth_write(char *buf)
433{ 546{
434 int status = bluetooth_status(ibm); 547 int status = bluetooth_status();
435 char *cmd; 548 char *cmd;
436 int do_cmd = 0; 549 int do_cmd = 0;
437 550
438 if (!ibm->supported) 551 if (!bluetooth_supported)
439 return -EINVAL; 552 return -ENODEV;
440 553
441 while ((cmd = next_cmd(&buf))) { 554 while ((cmd = next_cmd(&buf))) {
442 if (strlencmp(cmd, "enable") == 0) { 555 if (strlencmp(cmd, "enable") == 0) {
@@ -454,58 +567,161 @@ static int bluetooth_write(struct ibm_struct *ibm, char *buf)
454 return 0; 567 return 0;
455} 568}
456 569
457static 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)
458{ 578{
459 if (!acpi_evalf(vid_handle, &ibm->state.video.autoswitch, "^VDEE", "d")) 579 int ivga;
460 return -ENODEV; 580
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;
461 597
462 return 0; 598 return 0;
463} 599}
464 600
465static int video_status(struct ibm_struct *ibm) 601static int video_status(void)
466{ 602{
467 int status = 0; 603 int status = 0;
468 int i; 604 int i;
469 605
470 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); 606 if (video_supported == VIDEO_570) {
471 if (acpi_evalf(NULL, &i, "\\VCDC", "d")) 607 if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
472 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 }
473 625
474 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); 626 return status;
475 if (acpi_evalf(NULL, &i, "\\VCDL", "d")) 627}
476 status |= 0x01 * i;
477 if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
478 status |= 0x08 * i;
479 628
480 if (acpi_evalf(vid_handle, &i, "^VDEE", "d")) 629static int video_autosw(void)
481 status |= 0x10 * (i & 1); 630{
631 int autosw = 0;
482 632
483 return status; 633 if (video_supported == VIDEO_570)
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");
637
638 return autosw & 1;
484} 639}
485 640
486static int video_read(struct ibm_struct *ibm, char *p) 641static int video_read(char *p)
487{ 642{
488 int status = video_status(ibm); 643 int status = video_status();
644 int autosw = video_autosw();
489 int len = 0; 645 int len = 0;
490 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");
491 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));
492 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));
493 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); 655 if (video_supported == VIDEO_NEW)
494 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));
495 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable, " 657 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
496 "crt_enable, crt_disable\n"); 658 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
497 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable, " 659 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
498 "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");
499 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); 663 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
500 664
501 return len; 665 return len;
502} 666}
503 667
504static 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)
505{ 718{
506 char *cmd; 719 char *cmd;
507 int enable, disable, status; 720 int enable, disable, status;
508 721
722 if (!video_supported)
723 return -ENODEV;
724
509 enable = disable = 0; 725 enable = disable = 0;
510 726
511 while ((cmd = next_cmd(&buf))) { 727 while ((cmd = next_cmd(&buf))) {
@@ -517,9 +733,11 @@ static int video_write(struct ibm_struct *ibm, char *buf)
517 enable |= 0x02; 733 enable |= 0x02;
518 } else if (strlencmp(cmd, "crt_disable") == 0) { 734 } else if (strlencmp(cmd, "crt_disable") == 0) {
519 disable |= 0x02; 735 disable |= 0x02;
520 } else if (strlencmp(cmd, "dvi_enable") == 0) { 736 } else if (video_supported == VIDEO_NEW &&
737 strlencmp(cmd, "dvi_enable") == 0) {
521 enable |= 0x08; 738 enable |= 0x08;
522 } else if (strlencmp(cmd, "dvi_disable") == 0) { 739 } else if (video_supported == VIDEO_NEW &&
740 strlencmp(cmd, "dvi_disable") == 0) {
523 disable |= 0x08; 741 disable |= 0x08;
524 } else if (strlencmp(cmd, "auto_enable") == 0) { 742 } else if (strlencmp(cmd, "auto_enable") == 0) {
525 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) 743 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
@@ -528,70 +746,75 @@ static int video_write(struct ibm_struct *ibm, char *buf)
528 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) 746 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
529 return -EIO; 747 return -EIO;
530 } else if (strlencmp(cmd, "video_switch") == 0) { 748 } else if (strlencmp(cmd, "video_switch") == 0) {
531 int autoswitch; 749 if (!video_switch())
532 if (!acpi_evalf(vid_handle, &autoswitch, "^VDEE", "d"))
533 return -EIO;
534 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
535 return -EIO;
536 if (!acpi_evalf(vid_handle, NULL, "VSWT", "v"))
537 return -EIO;
538 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd",
539 autoswitch))
540 return -EIO; 750 return -EIO;
541 } else if (strlencmp(cmd, "expand_toggle") == 0) { 751 } else if (strlencmp(cmd, "expand_toggle") == 0) {
542 if (!acpi_evalf(NULL, NULL, "\\VEXP", "v")) 752 if (!video_expand())
543 return -EIO; 753 return -EIO;
544 } else 754 } else
545 return -EINVAL; 755 return -EINVAL;
546 } 756 }
547 757
548 if (enable || disable) { 758 if (enable || disable) {
549 status = (video_status(ibm) & 0x0f & ~disable) | enable; 759 status = (video_status() & 0x0f & ~disable) | enable;
550 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80)) 760 if (!video_switch2(status))
551 return -EIO;
552 if (!acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1))
553 return -EIO; 761 return -EIO;
554 } 762 }
555 763
556 return 0; 764 return 0;
557} 765}
558 766
559static void video_exit(struct ibm_struct *ibm) 767static void video_exit(void)
560{ 768{
561 acpi_evalf(vid_handle, NULL, "_DOS", "vd", ibm->state.video.autoswitch); 769 acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
562} 770}
563 771
564static int light_init(struct ibm_struct *ibm) 772static int light_supported;
773static int light_status_supported;
774
775static int light_init(void)
565{ 776{
566 /* kblt not supported on G40, R32, X20 */ 777 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
567 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");
568 785
569 return 0; 786 return 0;
570} 787}
571 788
572static int light_read(struct ibm_struct *ibm, char *p) 789static int light_read(char *p)
573{ 790{
574 int len = 0; 791 int len = 0;
575 int status = 0; 792 int status = 0;
576 793
577 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 {
578 if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) 800 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
579 return -EIO; 801 return -EIO;
580 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));
581 } else 803 len += sprintf(p + len, "commands:\ton, off\n");
582 len += sprintf(p + len, "status:\t\tunknown\n"); 804 }
583
584 len += sprintf(p + len, "commands:\ton, off\n");
585 805
586 return len; 806 return len;
587} 807}
588 808
589static int light_write(struct ibm_struct *ibm, char *buf) 809static int light_write(char *buf)
590{ 810{
591 int cmos_cmd, lght_cmd; 811 int cmos_cmd, lght_cmd;
592 char *cmd; 812 char *cmd;
593 int success; 813 int success;
594 814
815 if (!light_supported)
816 return -ENODEV;
817
595 while ((cmd = next_cmd(&buf))) { 818 while ((cmd = next_cmd(&buf))) {
596 if (strlencmp(cmd, "on") == 0) { 819 if (strlencmp(cmd, "on") == 0) {
597 cmos_cmd = 0x0c; 820 cmos_cmd = 0x0c;
@@ -624,7 +847,7 @@ static int _sta(acpi_handle handle)
624 847
625#define dock_docked() (_sta(dock_handle) & 1) 848#define dock_docked() (_sta(dock_handle) & 1)
626 849
627static int dock_read(struct ibm_struct *ibm, char *p) 850static int dock_read(char *p)
628{ 851{
629 int len = 0; 852 int len = 0;
630 int docked = dock_docked(); 853 int docked = dock_docked();
@@ -641,18 +864,17 @@ static int dock_read(struct ibm_struct *ibm, char *p)
641 return len; 864 return len;
642} 865}
643 866
644static int dock_write(struct ibm_struct *ibm, char *buf) 867static int dock_write(char *buf)
645{ 868{
646 char *cmd; 869 char *cmd;
647 870
648 if (!dock_docked()) 871 if (!dock_docked())
649 return -EINVAL; 872 return -ENODEV;
650 873
651 while ((cmd = next_cmd(&buf))) { 874 while ((cmd = next_cmd(&buf))) {
652 if (strlencmp(cmd, "undock") == 0) { 875 if (strlencmp(cmd, "undock") == 0) {
653 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0)) 876 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
654 return -EIO; 877 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
655 if (!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
656 return -EIO; 878 return -EIO;
657 } else if (strlencmp(cmd, "dock") == 0) { 879 } else if (strlencmp(cmd, "dock") == 0) {
658 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) 880 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
@@ -667,8 +889,13 @@ static int dock_write(struct ibm_struct *ibm, char *buf)
667static void dock_notify(struct ibm_struct *ibm, u32 event) 889static void dock_notify(struct ibm_struct *ibm, u32 event)
668{ 890{
669 int docked = dock_docked(); 891 int docked = dock_docked();
892 int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
670 893
671 if (event == 3 && docked) 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)
672 acpi_bus_generate_event(ibm->device, event, 1); /* button */ 899 acpi_bus_generate_event(ibm->device, event, 1); /* button */
673 else if (event == 3 && !docked) 900 else if (event == 3 && !docked)
674 acpi_bus_generate_event(ibm->device, event, 2); /* undock */ 901 acpi_bus_generate_event(ibm->device, event, 2); /* undock */
@@ -681,42 +908,69 @@ static void dock_notify(struct ibm_struct *ibm, u32 event)
681 } 908 }
682} 909}
683 910
684#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;
685 915
686static int bay_init(struct ibm_struct *ibm) 916static int bay_init(void)
687{ 917{
688 /* bay not supported on A21e, A22p, A31, A31p, G40, R32, R40e */ 918 bay_status_supported = bay_handle &&
689 ibm->supported = bay_handle && bayej_handle &&
690 acpi_evalf(bay_handle, NULL, "_STA", "qv"); 919 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);
691 927
692 return 0; 928 return 0;
693} 929}
694 930
695static 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)
696{ 934{
697 int len = 0; 935 int len = 0;
698 int occupied = bay_occupied(); 936 int occupied = bay_occupied(bay);
699 937 int occupied2 = bay_occupied(bay2);
700 if (!ibm->supported) 938 int eject, eject2;
701 len += sprintf(p + len, "status:\t\tnot supported\n"); 939
702 else if (!occupied) 940 len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ?
703 len += sprintf(p + len, "status:\t\tunoccupied\n"); 941 (occupied ? "occupied" : "unoccupied") :
704 else { 942 "not supported");
705 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)
706 len += sprintf(p + len, "commands:\teject\n"); 953 len += sprintf(p + len, "commands:\teject\n");
707 } 954 else if (eject2)
955 len += sprintf(p + len, "commands:\teject2\n");
708 956
709 return len; 957 return len;
710} 958}
711 959
712static int bay_write(struct ibm_struct *ibm, char *buf) 960static int bay_write(char *buf)
713{ 961{
714 char *cmd; 962 char *cmd;
715 963
964 if (!bay_eject_supported && !bay_eject2_supported)
965 return -ENODEV;
966
716 while ((cmd = next_cmd(&buf))) { 967 while ((cmd = next_cmd(&buf))) {
717 if (strlencmp(cmd, "eject") == 0) { 968 if (bay_eject_supported && strlencmp(cmd, "eject") == 0) {
718 if (!ibm->supported || 969 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
719 !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))
720 return -EIO; 974 return -EIO;
721 } else 975 } else
722 return -EINVAL; 976 return -EINVAL;
@@ -730,22 +984,31 @@ static void bay_notify(struct ibm_struct *ibm, u32 event)
730 acpi_bus_generate_event(ibm->device, event, 0); 984 acpi_bus_generate_event(ibm->device, event, 0);
731} 985}
732 986
733static int cmos_read(struct ibm_struct *ibm, char *p) 987static int cmos_read(char *p)
734{ 988{
735 int len = 0; 989 int len = 0;
736 990
737 /* 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 */
738 if (!cmos_handle) 993 if (!cmos_handle)
739 len += sprintf(p + len, "status:\t\tnot supported\n"); 994 len += sprintf(p + len, "status:\t\tnot supported\n");
740 else { 995 else {
741 len += sprintf(p + len, "status:\t\tsupported\n"); 996 len += sprintf(p + len, "status:\t\tsupported\n");
742 len += sprintf(p + len, "commands:\t<int>\n"); 997 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
743 } 998 }
744 999
745 return len; 1000 return len;
746} 1001}
747 1002
748static 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)
749{ 1012{
750 char *cmd; 1013 char *cmd;
751 int cmos_cmd; 1014 int cmos_cmd;
@@ -754,64 +1017,296 @@ static int cmos_write(struct ibm_struct *ibm, char *buf)
754 return -EINVAL; 1017 return -EINVAL;
755 1018
756 while ((cmd = next_cmd(&buf))) { 1019 while ((cmd = next_cmd(&buf))) {
757 if (sscanf(cmd, "%u", &cmos_cmd) == 1) { 1020 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
1021 cmos_cmd >= 0 && cmos_cmd <= 21) {
758 /* cmos_cmd set */ 1022 /* cmos_cmd set */
759 } else 1023 } else
760 return -EINVAL; 1024 return -EINVAL;
761 1025
762 if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) 1026 if (!cmos_eval(cmos_cmd))
763 return -EIO; 1027 return -EIO;
764 } 1028 }
765 1029
766 return 0; 1030 return 0;
767} 1031}
768 1032
769static 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)
770{ 1060{
771 int len = 0; 1061 int len = 0;
772 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
773 len += sprintf(p + len, "commands:\t" 1081 len += sprintf(p + len, "commands:\t"
774 "<int> on, <int> off, <int> blink\n"); 1082 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
775 1083
776 return len; 1084 return len;
777} 1085}
778 1086
779static 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)
780{ 1098{
781 char *cmd; 1099 char *cmd;
782 unsigned int led; 1100 int led, ind, ret;
783 int led_cmd, sysl_cmd, bled_a, bled_b; 1101
1102 if (!led_supported)
1103 return -ENODEV;
784 1104
785 while ((cmd = next_cmd(&buf))) { 1105 while ((cmd = next_cmd(&buf))) {
786 if (sscanf(cmd, "%u", &led) != 1) 1106 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
787 return -EINVAL; 1107 return -EINVAL;
788 1108
789 if (strstr(cmd, "blink")) { 1109 if (strstr(cmd, "off")) {
790 led_cmd = 0xc0; 1110 ind = 0;
791 sysl_cmd = 2;
792 bled_a = 2;
793 bled_b = 1;
794 } else if (strstr(cmd, "on")) { 1111 } else if (strstr(cmd, "on")) {
795 led_cmd = 0x80; 1112 ind = 1;
796 sysl_cmd = 1; 1113 } else if (strstr(cmd, "blink")) {
797 bled_a = 2; 1114 ind = 2;
798 bled_b = 0;
799 } else if (strstr(cmd, "off")) {
800 led_cmd = sysl_cmd = bled_a = bled_b = 0;
801 } else 1115 } else
802 return -EINVAL; 1116 return -EINVAL;
803 1117
804 if (led_handle) { 1118 if (led_supported == LED_570) {
1119 /* 570 */
1120 led = 1 << led;
805 if (!acpi_evalf(led_handle, NULL, NULL, "vdd", 1121 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
806 led, led_cmd)) 1122 led, led_sled_arg1[ind]))
807 return -EIO; 1123 return -EIO;
808 } else if (led < 2) { 1124 } else if (led_supported == LED_OLD) {
809 if (acpi_evalf(sysl_handle, NULL, NULL, "vdd", 1125 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
810 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]))
811 return -EIO; 1140 return -EIO;
812 } else if (led == 2 && bled_handle) { 1141 }
813 if (acpi_evalf(bled_handle, NULL, NULL, "vdd", 1142 }
814 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))
815 return -EIO; 1310 return -EIO;
816 } else 1311 } else
817 return -EINVAL; 1312 return -EINVAL;
@@ -820,28 +1315,224 @@ static int led_write(struct ibm_struct *ibm, char *buf)
820 return 0; 1315 return 0;
821} 1316}
822 1317
823static int beep_read(struct ibm_struct *ibm, char *p) 1318static int brightness_offset = 0x31;
1319
1320static int brightness_read(char *p)
824{ 1321{
825 int len = 0; 1322 int len = 0;
1323 u8 level;
826 1324
827 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 }
828 1333
829 return len; 1334 return len;
830} 1335}
831 1336
832static 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)
833{ 1341{
1342 int cmos_cmd, inc, i;
1343 u8 level;
1344 int new_level;
834 char *cmd; 1345 char *cmd;
835 int beep_cmd;
836 1346
837 while ((cmd = next_cmd(&buf))) { 1347 while ((cmd = next_cmd(&buf))) {
838 if (sscanf(cmd, "%u", &beep_cmd) == 1) { 1348 if (!acpi_ec_read(brightness_offset, &level))
839 /* 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;
840 } else 1427 } else
841 return -EINVAL; 1428 return -EINVAL;
842 1429
843 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"))
844 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;
845 } 1536 }
846 1537
847 return 0; 1538 return 0;
@@ -855,7 +1546,7 @@ static struct ibm_struct ibms[] = {
855 }, 1546 },
856 { 1547 {
857 .name = "hotkey", 1548 .name = "hotkey",
858 .hid = "IBM0068", 1549 .hid = IBM_HKEY_HID,
859 .init = hotkey_init, 1550 .init = hotkey_init,
860 .read = hotkey_read, 1551 .read = hotkey_read,
861 .write = hotkey_write, 1552 .write = hotkey_write,
@@ -892,6 +1583,13 @@ static struct ibm_struct ibms[] = {
892 .type = ACPI_SYSTEM_NOTIFY, 1583 .type = ACPI_SYSTEM_NOTIFY,
893 }, 1584 },
894 { 1585 {
1586 .name = "dock",
1587 .hid = IBM_PCI_HID,
1588 .notify = dock_notify,
1589 .handle = &pci_handle,
1590 .type = ACPI_SYSTEM_NOTIFY,
1591 },
1592 {
895 .name = "bay", 1593 .name = "bay",
896 .init = bay_init, 1594 .init = bay_init,
897 .read = bay_read, 1595 .read = bay_read,
@@ -904,24 +1602,49 @@ static struct ibm_struct ibms[] = {
904 .name = "cmos", 1602 .name = "cmos",
905 .read = cmos_read, 1603 .read = cmos_read,
906 .write = cmos_write, 1604 .write = cmos_write,
907 .experimental = 1,
908 }, 1605 },
909 { 1606 {
910 .name = "led", 1607 .name = "led",
1608 .init = led_init,
911 .read = led_read, 1609 .read = led_read,
912 .write = led_write, 1610 .write = led_write,
913 .experimental = 1,
914 }, 1611 },
915 { 1612 {
916 .name = "beep", 1613 .name = "beep",
917 .read = beep_read, 1614 .read = beep_read,
918 .write = beep_write, 1615 .write = beep_write,
1616 },
1617 {
1618 .name = "thermal",
1619 .init = thermal_init,
1620 .read = thermal_read,
1621 },
1622 {
1623 .name = "ecdump",
1624 .read = ecdump_read,
1625 .write = ecdump_write,
1626 .experimental = 1,
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,
919 .experimental = 1, 1644 .experimental = 1,
920 }, 1645 },
921}; 1646};
922 1647
923#define NUM_IBMS (sizeof(ibms)/sizeof(ibms[0]))
924
925static 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,
926 int *eof, void *data) 1649 int *eof, void *data)
927{ 1650{
@@ -931,7 +1654,7 @@ static int dispatch_read(char *page, char **start, off_t off, int count,
931 if (!ibm || !ibm->read) 1654 if (!ibm || !ibm->read)
932 return -EINVAL; 1655 return -EINVAL;
933 1656
934 len = ibm->read(ibm, page); 1657 len = ibm->read(page);
935 if (len < 0) 1658 if (len < 0)
936 return len; 1659 return len;
937 1660
@@ -968,7 +1691,7 @@ static int dispatch_write(struct file *file, const char __user * userbuf,
968 1691
969 kernbuf[count] = 0; 1692 kernbuf[count] = 0;
970 strcat(kernbuf, ","); 1693 strcat(kernbuf, ",");
971 ret = ibm->write(ibm, kernbuf); 1694 ret = ibm->write(kernbuf);
972 if (ret == 0) 1695 if (ret == 0)
973 ret = count; 1696 ret = count;
974 1697
@@ -987,7 +1710,7 @@ static void dispatch_notify(acpi_handle handle, u32 event, void *data)
987 ibm->notify(ibm, event); 1710 ibm->notify(ibm, event);
988} 1711}
989 1712
990static int setup_notify(struct ibm_struct *ibm) 1713static int __init setup_notify(struct ibm_struct *ibm)
991{ 1714{
992 acpi_status status; 1715 acpi_status status;
993 int ret; 1716 int ret;
@@ -1012,17 +1735,15 @@ static int setup_notify(struct ibm_struct *ibm)
1012 return -ENODEV; 1735 return -ENODEV;
1013 } 1736 }
1014 1737
1015 ibm->notify_installed = 1;
1016
1017 return 0; 1738 return 0;
1018} 1739}
1019 1740
1020static int ibmacpi_device_add(struct acpi_device *device) 1741static int __init ibm_device_add(struct acpi_device *device)
1021{ 1742{
1022 return 0; 1743 return 0;
1023} 1744}
1024 1745
1025static int register_driver(struct ibm_struct *ibm) 1746static int __init register_driver(struct ibm_struct *ibm)
1026{ 1747{
1027 int ret; 1748 int ret;
1028 1749
@@ -1035,7 +1756,7 @@ static int register_driver(struct ibm_struct *ibm)
1035 memset(ibm->driver, 0, sizeof(struct acpi_driver)); 1756 memset(ibm->driver, 0, sizeof(struct acpi_driver));
1036 sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); 1757 sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);
1037 ibm->driver->ids = ibm->hid; 1758 ibm->driver->ids = ibm->hid;
1038 ibm->driver->ops.add = &ibmacpi_device_add; 1759 ibm->driver->ops.add = &ibm_device_add;
1039 1760
1040 ret = acpi_bus_register_driver(ibm->driver); 1761 ret = acpi_bus_register_driver(ibm->driver);
1041 if (ret < 0) { 1762 if (ret < 0) {
@@ -1047,7 +1768,7 @@ static int register_driver(struct ibm_struct *ibm)
1047 return ret; 1768 return ret;
1048} 1769}
1049 1770
1050static int ibm_init(struct ibm_struct *ibm) 1771static int __init ibm_init(struct ibm_struct *ibm)
1051{ 1772{
1052 int ret; 1773 int ret;
1053 struct proc_dir_entry *entry; 1774 struct proc_dir_entry *entry;
@@ -1063,31 +1784,34 @@ static int ibm_init(struct ibm_struct *ibm)
1063 } 1784 }
1064 1785
1065 if (ibm->init) { 1786 if (ibm->init) {
1066 ret = ibm->init(ibm); 1787 ret = ibm->init();
1067 if (ret != 0) 1788 if (ret != 0)
1068 return ret; 1789 return ret;
1069 ibm->init_called = 1; 1790 ibm->init_called = 1;
1070 } 1791 }
1071 1792
1072 entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR, 1793 if (ibm->read) {
1073 proc_dir); 1794 entry = create_proc_entry(ibm->name,
1074 if (!entry) { 1795 S_IFREG | S_IRUGO | S_IWUSR,
1075 printk(IBM_ERR "unable to create proc entry %s\n", ibm->name); 1796 proc_dir);
1076 return -ENODEV; 1797 if (!entry) {
1077 } 1798 printk(IBM_ERR "unable to create proc entry %s\n",
1078 entry->owner = THIS_MODULE; 1799 ibm->name);
1079 ibm->proc_created = 1; 1800 return -ENODEV;
1080 1801 }
1081 entry->data = ibm; 1802 entry->owner = THIS_MODULE;
1082 if (ibm->read) 1803 entry->data = ibm;
1083 entry->read_proc = &dispatch_read; 1804 entry->read_proc = &dispatch_read;
1084 if (ibm->write) 1805 if (ibm->write)
1085 entry->write_proc = &dispatch_write; 1806 entry->write_proc = &dispatch_write;
1807 ibm->proc_created = 1;
1808 }
1086 1809
1087 if (ibm->notify) { 1810 if (ibm->notify) {
1088 ret = setup_notify(ibm); 1811 ret = setup_notify(ibm);
1089 if (ret < 0) 1812 if (ret < 0)
1090 return ret; 1813 return ret;
1814 ibm->notify_installed = 1;
1091 } 1815 }
1092 1816
1093 return 0; 1817 return 0;
@@ -1103,7 +1827,7 @@ static void ibm_exit(struct ibm_struct *ibm)
1103 remove_proc_entry(ibm->name, proc_dir); 1827 remove_proc_entry(ibm->name, proc_dir);
1104 1828
1105 if (ibm->init_called && ibm->exit) 1829 if (ibm->init_called && ibm->exit)
1106 ibm->exit(ibm); 1830 ibm->exit();
1107 1831
1108 if (ibm->driver_registered) { 1832 if (ibm->driver_registered) {
1109 acpi_bus_unregister_driver(ibm->driver); 1833 acpi_bus_unregister_driver(ibm->driver);
@@ -1111,59 +1835,66 @@ static void ibm_exit(struct ibm_struct *ibm)
1111 } 1835 }
1112} 1836}
1113 1837
1114static int ibm_handle_init(char *name, 1838static void __init ibm_handle_init(char *name,
1115 acpi_handle * handle, acpi_handle parent, 1839 acpi_handle * handle, acpi_handle parent,
1116 char **paths, int num_paths, int required) 1840 char **paths, int num_paths, char **path)
1117{ 1841{
1118 int i; 1842 int i;
1119 acpi_status status; 1843 acpi_status status;
1120 1844
1121 for (i = 0; i < num_paths; i++) { 1845 for (i = 0; i < num_paths; i++) {
1122 status = acpi_get_handle(parent, paths[i], handle); 1846 status = acpi_get_handle(parent, paths[i], handle);
1123 if (ACPI_SUCCESS(status)) 1847 if (ACPI_SUCCESS(status)) {
1124 return 0; 1848 *path = paths[i];
1849 return;
1850 }
1125 } 1851 }
1126 1852
1127 *handle = NULL; 1853 *handle = NULL;
1128
1129 if (required) {
1130 printk(IBM_ERR "%s object not found\n", name);
1131 return -1;
1132 }
1133
1134 return 0;
1135} 1854}
1136 1855
1137#define IBM_HANDLE_INIT(object, required) \ 1856#define IBM_HANDLE_INIT(object) \
1138 ibm_handle_init(#object, &object##_handle, *object##_parent, \ 1857 ibm_handle_init(#object, &object##_handle, *object##_parent, \
1139 object##_paths, sizeof(object##_paths)/sizeof(char*), required) 1858 object##_paths, ARRAY_SIZE(object##_paths), &object##_path)
1140 1859
1141static int set_ibm_param(const char *val, struct kernel_param *kp) 1860static int set_ibm_param(const char *val, struct kernel_param *kp)
1142{ 1861{
1143 unsigned int i; 1862 unsigned int i;
1144 char arg_with_comma[32];
1145
1146 if (strlen(val) > 30)
1147 return -ENOSPC;
1148 1863
1149 strcpy(arg_with_comma, val); 1864 for (i = 0; i < ARRAY_SIZE(ibms); i++)
1150 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 }
1151 1872
1152 for (i = 0; i < NUM_IBMS; i++)
1153 if (strcmp(ibms[i].name, kp->name) == 0)
1154 return ibms[i].write(&ibms[i], arg_with_comma);
1155 BUG();
1156 return -EINVAL; 1873 return -EINVAL;
1157} 1874}
1158 1875
1159#define IBM_PARAM(feature) \ 1876#define IBM_PARAM(feature) \
1160 module_param_call(feature, set_ibm_param, NULL, NULL, 0) 1877 module_param_call(feature, set_ibm_param, NULL, NULL, 0)
1161 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
1162static void acpi_ibm_exit(void) 1893static void acpi_ibm_exit(void)
1163{ 1894{
1164 int i; 1895 int i;
1165 1896
1166 for (i = NUM_IBMS - 1; i >= 0; i--) 1897 for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--)
1167 ibm_exit(&ibms[i]); 1898 ibm_exit(&ibms[i]);
1168 1899
1169 remove_proc_entry(IBM_DIR, acpi_root_dir); 1900 remove_proc_entry(IBM_DIR, acpi_root_dir);
@@ -1177,28 +1908,39 @@ static int __init acpi_ibm_init(void)
1177 return -ENODEV; 1908 return -ENODEV;
1178 1909
1179 if (!acpi_specific_hotkey_enabled) { 1910 if (!acpi_specific_hotkey_enabled) {
1180 printk(IBM_ERR "Using generic hotkey driver\n"); 1911 printk(IBM_ERR "using generic hotkey driver\n");
1181 return -ENODEV; 1912 return -ENODEV;
1182 } 1913 }
1183 /* these handles are required */
1184 if (IBM_HANDLE_INIT(ec, 1) < 0 ||
1185 IBM_HANDLE_INIT(hkey, 1) < 0 ||
1186 IBM_HANDLE_INIT(vid, 1) < 0 || IBM_HANDLE_INIT(beep, 1) < 0)
1187 return -ENODEV;
1188 1914
1189 /* these handles have alternatives */ 1915 /* ec is required because many other handles are relative to it */
1190 IBM_HANDLE_INIT(lght, 0); 1916 IBM_HANDLE_INIT(ec);
1191 if (IBM_HANDLE_INIT(cmos, !lght_handle) < 0) 1917 if (!ec_handle) {
1192 return -ENODEV; 1918 printk(IBM_ERR "ec object not found\n");
1193 IBM_HANDLE_INIT(sysl, 0);
1194 if (IBM_HANDLE_INIT(led, !sysl_handle) < 0)
1195 return -ENODEV; 1919 return -ENODEV;
1920 }
1196 1921
1197 /* these handles are not required */ 1922 /* these handles are not required */
1198 IBM_HANDLE_INIT(dock, 0); 1923 IBM_HANDLE_INIT(vid);
1199 IBM_HANDLE_INIT(bay, 0); 1924 IBM_HANDLE_INIT(vid2);
1200 IBM_HANDLE_INIT(bayej, 0); 1925 IBM_HANDLE_INIT(ledb);
1201 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);
1202 1944
1203 proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); 1945 proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
1204 if (!proc_dir) { 1946 if (!proc_dir) {
@@ -1207,8 +1949,10 @@ static int __init acpi_ibm_init(void)
1207 } 1949 }
1208 proc_dir->owner = THIS_MODULE; 1950 proc_dir->owner = THIS_MODULE;
1209 1951
1210 for (i = 0; i < NUM_IBMS; i++) { 1952 for (i = 0; i < ARRAY_SIZE(ibms); i++) {
1211 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);
1212 if (ret < 0) { 1956 if (ret < 0) {
1213 acpi_ibm_exit(); 1957 acpi_ibm_exit();
1214 return ret; 1958 return ret;
@@ -1220,17 +1964,3 @@ static int __init acpi_ibm_init(void)
1220 1964
1221module_init(acpi_ibm_init); 1965module_init(acpi_ibm_init);
1222module_exit(acpi_ibm_exit); 1966module_exit(acpi_ibm_exit);
1223
1224MODULE_AUTHOR("Borislav Deianov");
1225MODULE_DESCRIPTION(IBM_DESC);
1226MODULE_LICENSE("GPL");
1227
1228IBM_PARAM(hotkey);
1229IBM_PARAM(bluetooth);
1230IBM_PARAM(video);
1231IBM_PARAM(light);
1232IBM_PARAM(dock);
1233IBM_PARAM(bay);
1234IBM_PARAM(cmos);
1235IBM_PARAM(led);
1236IBM_PARAM(beep);