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