|
|
|
@ -5,49 +5,95 @@ with lib; |
|
|
|
|
let |
|
|
|
|
|
|
|
|
|
cfg = config.services.thinkfan; |
|
|
|
|
configFile = pkgs.writeText "thinkfan.conf" '' |
|
|
|
|
# ATTENTION: There is only very basic sanity checking on the configuration. |
|
|
|
|
# That means you can set your temperature limits as insane as you like. You |
|
|
|
|
# can do anything stupid, e.g. turn off your fan when your CPU reaches 70°C. |
|
|
|
|
# |
|
|
|
|
# That's why this program is called THINKfan: You gotta think for yourself. |
|
|
|
|
# |
|
|
|
|
###################################################################### |
|
|
|
|
# |
|
|
|
|
# IBM/Lenovo Thinkpads (thinkpad_acpi, /proc/acpi/ibm) |
|
|
|
|
# ==================================================== |
|
|
|
|
# |
|
|
|
|
# IMPORTANT: |
|
|
|
|
# |
|
|
|
|
# To keep your HD from overheating, you have to specify a correction value for |
|
|
|
|
# the sensor that has the HD's temperature. You need to do this because |
|
|
|
|
# thinkfan uses only the highest temperature it can find in the system, and |
|
|
|
|
# that'll most likely never be your HD, as most HDs are already out of spec |
|
|
|
|
# when they reach 55 °C. |
|
|
|
|
# Correction values are applied from left to right in the same order as the |
|
|
|
|
# temperatures are read from the file. |
|
|
|
|
# |
|
|
|
|
# For example: |
|
|
|
|
# tp_thermal /proc/acpi/ibm/thermal (0, 0, 10) |
|
|
|
|
# will add a fixed value of 10 °C the 3rd value read from that file. Check out |
|
|
|
|
# http://www.thinkwiki.org/wiki/Thermal_Sensors to find out how much you may |
|
|
|
|
# want to add to certain temperatures. |
|
|
|
|
|
|
|
|
|
${cfg.fan} |
|
|
|
|
${cfg.sensors} |
|
|
|
|
|
|
|
|
|
# Syntax: |
|
|
|
|
# (LEVEL, LOW, HIGH) |
|
|
|
|
# LEVEL is the fan level to use (0-7 with thinkpad_acpi) |
|
|
|
|
# LOW is the temperature at which to step down to the previous level |
|
|
|
|
# HIGH is the temperature at which to step up to the next level |
|
|
|
|
# All numbers are integers. |
|
|
|
|
# |
|
|
|
|
|
|
|
|
|
${cfg.levels} |
|
|
|
|
''; |
|
|
|
|
settingsFormat = pkgs.formats.yaml { }; |
|
|
|
|
configFile = settingsFormat.generate "thinkfan.yaml" cfg.settings; |
|
|
|
|
thinkfan = pkgs.thinkfan.override { inherit (cfg) smartSupport; }; |
|
|
|
|
|
|
|
|
|
# fan-speed and temperature levels |
|
|
|
|
levelType = with types; |
|
|
|
|
let |
|
|
|
|
tuple = ts: mkOptionType { |
|
|
|
|
name = "tuple"; |
|
|
|
|
merge = mergeOneOption; |
|
|
|
|
check = xs: all id (zipListsWith (t: x: t.check x) ts xs); |
|
|
|
|
description = "tuple of" + concatMapStrings (t: " (${t.description})") ts; |
|
|
|
|
}; |
|
|
|
|
level = ints.unsigned; |
|
|
|
|
special = enum [ "level auto" "level full-speed" "level disengage" ]; |
|
|
|
|
in |
|
|
|
|
tuple [ (either level special) level level ]; |
|
|
|
|
|
|
|
|
|
# sensor or fan config |
|
|
|
|
sensorType = name: types.submodule { |
|
|
|
|
freeformType = types.attrsOf settingsFormat.type; |
|
|
|
|
options = { |
|
|
|
|
type = mkOption { |
|
|
|
|
type = types.enum [ "hwmon" "atasmart" "tpacpi" "nvml" ]; |
|
|
|
|
description = '' |
|
|
|
|
The ${name} type, can be |
|
|
|
|
<literal>hwmon</literal> for standard ${name}s, |
|
|
|
|
|
|
|
|
|
thinkfan = pkgs.thinkfan.override { smartSupport = cfg.smartSupport; }; |
|
|
|
|
<literal>atasmart</literal> to read the temperature via |
|
|
|
|
S.M.A.R.T (requires smartSupport to be enabled), |
|
|
|
|
|
|
|
|
|
<literal>tpacpi</literal> for the legacy thinkpac_acpi driver, or |
|
|
|
|
|
|
|
|
|
<literal>nvml</literal> for the (proprietary) nVidia driver. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
query = mkOption { |
|
|
|
|
type = types.str; |
|
|
|
|
description = '' |
|
|
|
|
The query string used to match one or more ${name}s: can be |
|
|
|
|
a fullpath to the temperature file (single ${name}) or a fullpath |
|
|
|
|
to a driver directory (multiple ${name}s). |
|
|
|
|
|
|
|
|
|
<note><para> |
|
|
|
|
When multiple ${name}s match, the query can be restricted using the |
|
|
|
|
<option>name</option> or <option>indices</option> options. |
|
|
|
|
</para></note> |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
indices = mkOption { |
|
|
|
|
type = with types; nullOr (listOf ints.unsigned); |
|
|
|
|
default = null; |
|
|
|
|
description = '' |
|
|
|
|
A list of ${name}s to pick in case multiple ${name}s match the query. |
|
|
|
|
|
|
|
|
|
<note><para>Indices start from 0.</para></note> |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
} // optionalAttrs (name == "sensor") { |
|
|
|
|
correction = mkOption { |
|
|
|
|
type = with types; nullOr (listOf int); |
|
|
|
|
default = null; |
|
|
|
|
description = '' |
|
|
|
|
A list of values to be added to the temperature of each sensor, |
|
|
|
|
can be used to equalize small discrepancies in temperature ratings. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
# removes NixOS special and unused attributes |
|
|
|
|
sensorToConf = { type, query, ... }@args: |
|
|
|
|
(filterAttrs (k: v: v != null && !(elem k ["type" "query"])) args) |
|
|
|
|
// { "${type}" = query; }; |
|
|
|
|
|
|
|
|
|
syntaxNote = name: '' |
|
|
|
|
<note><para> |
|
|
|
|
This section slightly departs from the thinkfan.conf syntax. |
|
|
|
|
The type and path must be specified like this: |
|
|
|
|
<literal> |
|
|
|
|
type = "tpacpi"; |
|
|
|
|
query = "/proc/acpi/ibm/${name}"; |
|
|
|
|
</literal> |
|
|
|
|
instead of a single declaration like: |
|
|
|
|
<literal> |
|
|
|
|
- tpacpi: /proc/acpi/ibm/${name} |
|
|
|
|
</literal> |
|
|
|
|
</para></note> |
|
|
|
|
''; |
|
|
|
|
|
|
|
|
|
in { |
|
|
|
|
|
|
|
|
@ -59,76 +105,93 @@ in { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = false; |
|
|
|
|
description = '' |
|
|
|
|
Whether to enable thinkfan, fan controller for IBM/Lenovo ThinkPads. |
|
|
|
|
Whether to enable thinkfan, a fan control program. |
|
|
|
|
|
|
|
|
|
<note><para> |
|
|
|
|
This module targets IBM/Lenovo thinkpads by default, for |
|
|
|
|
other hardware you will have configure it more carefully. |
|
|
|
|
</para></note> |
|
|
|
|
''; |
|
|
|
|
relatedPackages = [ "thinkfan" ]; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
smartSupport = mkOption { |
|
|
|
|
type = types.bool; |
|
|
|
|
default = false; |
|
|
|
|
description = '' |
|
|
|
|
Whether to build thinkfan with SMART support to read temperatures |
|
|
|
|
Whether to build thinkfan with S.M.A.R.T. support to read temperatures |
|
|
|
|
directly from hard disks. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
sensors = mkOption { |
|
|
|
|
type = types.lines; |
|
|
|
|
default = '' |
|
|
|
|
tp_thermal /proc/acpi/ibm/thermal (0,0,10) |
|
|
|
|
''; |
|
|
|
|
description ='' |
|
|
|
|
thinkfan can read temperatures from three possible sources: |
|
|
|
|
|
|
|
|
|
/proc/acpi/ibm/thermal |
|
|
|
|
Which is provided by the thinkpad_acpi kernel |
|
|
|
|
module (keyword tp_thermal) |
|
|
|
|
|
|
|
|
|
/sys/class/hwmon/*/temp*_input |
|
|
|
|
Which may be provided by any hwmon drivers (keyword |
|
|
|
|
hwmon) |
|
|
|
|
|
|
|
|
|
S.M.A.R.T. (requires smartSupport to be enabled) |
|
|
|
|
Which reads the temperature directly from the hard |
|
|
|
|
disk using libatasmart (keyword atasmart) |
|
|
|
|
|
|
|
|
|
Multiple sensors may be added, in which case they will be |
|
|
|
|
numbered in their order of appearance. |
|
|
|
|
''; |
|
|
|
|
type = types.listOf (sensorType "sensor"); |
|
|
|
|
default = [ |
|
|
|
|
{ type = "tpacpi"; |
|
|
|
|
query = "/proc/acpi/ibm/thermal"; |
|
|
|
|
} |
|
|
|
|
]; |
|
|
|
|
description = '' |
|
|
|
|
List of temperature sensors thinkfan will monitor. |
|
|
|
|
'' + syntaxNote "thermal"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
fan = mkOption { |
|
|
|
|
type = types.str; |
|
|
|
|
default = "tp_fan /proc/acpi/ibm/fan"; |
|
|
|
|
description ='' |
|
|
|
|
Specifies the fan we want to use. |
|
|
|
|
On anything other than a Thinkpad you'll probably |
|
|
|
|
use some PWM control file in /sys/class/hwmon. |
|
|
|
|
A sysfs fan would be specified like this: |
|
|
|
|
pwm_fan /sys/class/hwmon/hwmon2/device/pwm1 |
|
|
|
|
''; |
|
|
|
|
fans = mkOption { |
|
|
|
|
type = types.listOf (sensorType "fan"); |
|
|
|
|
default = [ |
|
|
|
|
{ type = "tpacpi"; |
|
|
|
|
query = "/proc/acpi/ibm/fan"; |
|
|
|
|
} |
|
|
|
|
]; |
|
|
|
|
description = '' |
|
|
|
|
List of fans thinkfan will control. |
|
|
|
|
'' + syntaxNote "fan"; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
levels = mkOption { |
|
|
|
|
type = types.lines; |
|
|
|
|
default = '' |
|
|
|
|
(0, 0, 55) |
|
|
|
|
(1, 48, 60) |
|
|
|
|
(2, 50, 61) |
|
|
|
|
(3, 52, 63) |
|
|
|
|
(6, 56, 65) |
|
|
|
|
(7, 60, 85) |
|
|
|
|
(127, 80, 32767) |
|
|
|
|
''; |
|
|
|
|
type = types.listOf levelType; |
|
|
|
|
default = [ |
|
|
|
|
[0 0 55] |
|
|
|
|
[1 48 60] |
|
|
|
|
[2 50 61] |
|
|
|
|
[3 52 63] |
|
|
|
|
[6 56 65] |
|
|
|
|
[7 60 85] |
|
|
|
|
["level auto" 80 32767] |
|
|
|
|
]; |
|
|
|
|
description = '' |
|
|
|
|
(LEVEL, LOW, HIGH) |
|
|
|
|
LEVEL is the fan level to use (0-7 with thinkpad_acpi). |
|
|
|
|
[LEVEL LOW HIGH] |
|
|
|
|
|
|
|
|
|
LEVEL is the fan level to use: it can be an integer (0-7 with thinkpad_acpi), |
|
|
|
|
"level auto" (to keep the default firmware behavior), "level full-speed" or |
|
|
|
|
"level disengage" (to run the fan as fast as possible). |
|
|
|
|
LOW is the temperature at which to step down to the previous level. |
|
|
|
|
HIGH is the temperature at which to step up to the next level. |
|
|
|
|
All numbers are integers. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
extraArgs = mkOption { |
|
|
|
|
type = types.listOf types.str; |
|
|
|
|
default = [ ]; |
|
|
|
|
example = [ "-b" "0" ]; |
|
|
|
|
description = '' |
|
|
|
|
A list of extra command line arguments to pass to thinkfan. |
|
|
|
|
Check the thinkfan(1) manpage for available arguments. |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
settings = mkOption { |
|
|
|
|
type = types.attrsOf settingsFormat.type; |
|
|
|
|
default = { }; |
|
|
|
|
description = '' |
|
|
|
|
Thinkfan settings. Use this option to configure thinkfan |
|
|
|
|
settings not exposed in a NixOS option or to bypass one. |
|
|
|
|
Before changing this, read the <literal>thinkfan.conf(5)</literal> |
|
|
|
|
manpage and take a look at the example config file at |
|
|
|
|
<link xlink:href="https://github.com/vmatare/thinkfan/blob/master/examples/thinkfan.yaml"/> |
|
|
|
|
''; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -138,12 +201,21 @@ in { |
|
|
|
|
|
|
|
|
|
environment.systemPackages = [ thinkfan ]; |
|
|
|
|
|
|
|
|
|
systemd.services.thinkfan = { |
|
|
|
|
description = "Thinkfan"; |
|
|
|
|
after = [ "basic.target" ]; |
|
|
|
|
wantedBy = [ "multi-user.target" ]; |
|
|
|
|
path = [ thinkfan ]; |
|
|
|
|
serviceConfig.ExecStart = "${thinkfan}/bin/thinkfan -n -c ${configFile}"; |
|
|
|
|
services.thinkfan.settings = mapAttrs (k: v: mkDefault v) { |
|
|
|
|
sensors = map sensorToConf cfg.sensors; |
|
|
|
|
fans = map sensorToConf cfg.fans; |
|
|
|
|
levels = cfg.levels; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
systemd.packages = [ thinkfan ]; |
|
|
|
|
|
|
|
|
|
systemd.services = { |
|
|
|
|
thinkfan.environment.THINKFAN_ARGS = escapeShellArgs ([ "-c" configFile ] ++ cfg.extraArgs); |
|
|
|
|
|
|
|
|
|
# must be added manually, see issue #81138 |
|
|
|
|
thinkfan.wantedBy = [ "multi-user.target" ]; |
|
|
|
|
thinkfan-wakeup.wantedBy = [ "sleep.target" ]; |
|
|
|
|
thinkfan-sleep.wantedBy = [ "sleep.target" ]; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
boot.extraModprobeConfig = "options thinkpad_acpi experimental=1 fan_control=1"; |
|
|
|
|