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