On Mon, Nov 29, 2010 at 03:23:58PM +0100, Christopher Zimmermann wrote:
> Hi!
> 
> I'd like to implement fan speed control for Thinkpads. It is documented
> at http://www.thinkwiki.org/wiki/How_to_control_fan_speed#Hardware_specs
> and linux also implements this (but with special case for TP 570,
> 600e/x, 770e, 770x - anyone here with access to one of these?)
> Implementing a driver for this will be a piece of cake, but I need help
> with communication to userspace to get started. I guess the way to go is
> sysctl (?)

FWIW, I've hacked up that piece of cake for my X300 once.
I just used some spare function-keys (Fn-F1,F2,F3) to play with it.
(Diff attached)

However, since you can damage your hardware with this,
I'm not sure whether we want to add support for this into OpenBSD.


Index: acpithinkpad.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpithinkpad.c,v
retrieving revision 1.24
diff -p -u -p -u -r1.24 acpithinkpad.c
--- acpithinkpad.c      7 Aug 2010 16:21:20 -0000       1.24
+++ acpithinkpad.c      22 Aug 2010 15:43:22 -0000
@@ -86,6 +86,7 @@ struct acpithinkpad_softc {
 
        struct ksensor           sc_sens[THINKPAD_NSENSORS];
        struct ksensordev        sc_sensdev;
+       int                      cur_fanspeed;
 };
 
 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
@@ -102,6 +103,9 @@ int thinkpad_volume_up(struct acpithinkp
 int    thinkpad_volume_mute(struct acpithinkpad_softc *);
 int    thinkpad_brightness_up(struct acpithinkpad_softc *);
 int    thinkpad_brightness_down(struct acpithinkpad_softc *);
+int    thinkpad_get_fan(struct acpithinkpad_softc *);
+int    thinkpad_step_fan_up(struct acpithinkpad_softc *);
+int    thinkpad_step_fan_down(struct acpithinkpad_softc *);
 
 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
 void    thinkpad_sensor_refresh(void *);
@@ -114,6 +118,9 @@ struct cfdriver acpithinkpad_cd = {
        NULL, "acpithinkpad", DV_DULL
 };
 
+int fan_speeds[] = { 0, 1, 2, 3, 4, 5, 6, 7, 128 };
+#define MAX_FAN_SPEEDS 8
+
 const char *acpithinkpad_hids[] = { ACPI_DEV_THINKPAD, 0 };
 
 int
@@ -288,10 +295,19 @@ thinkpad_hotkey(struct aml_node *node, i
 #endif
                        handled = 1;
                        break;
-               case THINKPAD_BUTTON_HIBERNATE:
                case THINKPAD_BUTTON_FN_F1:
+                       thinkpad_get_fan(sc);
+                       handled = 1;
+                       break;
                case THINKPAD_BUTTON_LOCK_SCREEN:
+                       thinkpad_step_fan_down(sc);
+                       handled = 1;
+                       break;
                case THINKPAD_BUTTON_BATTERY_INFO:
+                       thinkpad_step_fan_up(sc);
+                       handled = 1;
+                       break;
+               case THINKPAD_BUTTON_HIBERNATE:
                case THINKPAD_BUTTON_FN_F6:
                case THINKPAD_BUTTON_EXTERNAL_SCREEN:
                case THINKPAD_BUTTON_POINTER_SWITCH:
@@ -417,6 +433,52 @@ int
 thinkpad_volume_up(struct acpithinkpad_softc *sc)
 {
        return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
+}
+
+int
+thinkpad_get_fan(struct acpithinkpad_softc *sc)
+{
+       u_int8_t buffer[2];
+
+       acpiec_read(sc->sc_acpi->sc_ec, 0x2f, 1, buffer);
+       printf("EC f...@0x2f: 0x%x\n", buffer[0]);
+
+       acpiec_read(sc->sc_acpi->sc_ec, 0x84, 2, buffer);
+
+       /* XXX LE only. But are there any BE thinkpads? */
+       printf("EC Fan Speed: %u RPM\n", *((u_int16_t*)buffer));
+
+       return (0);
+}
+
+int
+thinkpad_step_fan_up(struct acpithinkpad_softc *sc)
+{
+       if (sc->cur_fanspeed + 1 > MAX_FAN_SPEEDS)
+               return (-1);
+
+       sc->cur_fanspeed++;
+
+       printf("Adjust fan to: %u\n", fan_speeds[sc->cur_fanspeed]);
+       acpiec_write(sc->sc_acpi->sc_ec, 0x2f, 1,
+           (u_int8_t *)&fan_speeds[sc->cur_fanspeed]);
+
+       return (0);
+}
+
+int
+thinkpad_step_fan_down(struct acpithinkpad_softc *sc)
+{
+       if (sc->cur_fanspeed - 1 < 0)
+               return (-1);
+
+       sc->cur_fanspeed--;
+
+       printf("Adjust fan to: %u\n", fan_speeds[sc->cur_fanspeed]);
+       acpiec_write(sc->sc_acpi->sc_ec, 0x2f, 1,
+           (u_int8_t *)&fan_speeds[sc->cur_fanspeed]);
+
+       return (0);
 }
 
 int

Reply via email to