-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmsi_fan_curve.sh
More file actions
executable file
·182 lines (157 loc) · 5.74 KB
/
msi_fan_curve.sh
File metadata and controls
executable file
·182 lines (157 loc) · 5.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/bin/bash
# MSI Fan Curve Control
# Tested on GL65 Leopard 9SCXK (firmware 16U8EMS2.100)
EC_DEVICE="/dev/ec0"
BACKUP_DIR="$HOME/acpi_ec/backups"
# EC Register addresses
REG_CPU_TEMP=104 # 0x68
REG_CPU_FAN=112 # 0x70
REG_GPU_TEMP=128 # 0x80
REG_GPU_FAN=136 # 0x88
REG_FAN_MODE=244 # 0xF4
REG_COOLER_BOOST=152 # 0x98
# Curve registers
CPU_TEMP_REGS=(106 107 108 109 110 111) # 0x6A-0x6F
CPU_FAN_REGS=(114 115 116 117 118 119 120) # 0x72-0x78
GPU_TEMP_REGS=(130 131 132 133 134 135) # 0x82-0x87
GPU_FAN_REGS=(138 139 140 141 142 143 144) # 0x8A-0x90
# Profiles: "temp0,temp1,...,temp5|fan0,fan1,...,fan6"
declare -A PROFILES
PROFILES[silent_cpu]="55,62,70,78,85,90|20,30,45,60,75,90,100"
PROFILES[silent_gpu]="55,62,70,78,85,90|0,35,50,65,80,90,100"
PROFILES[balanced_cpu]="50,58,66,74,82,88|25,40,55,70,85,95,100"
PROFILES[balanced_gpu]="52,60,68,75,82,88|0,40,55,70,85,95,100"
PROFILES[performance_cpu]="45,55,65,75,82,88|35,50,65,80,90,100,100"
PROFILES[performance_gpu]="50,58,66,74,82,88|0,45,60,75,90,100,100"
PROFILES[gaming_cpu]="40,50,60,70,78,85|40,55,70,85,95,100,100"
PROFILES[gaming_gpu]="45,55,62,70,78,85|35,50,65,80,95,100,100"
check_root() {
[ "$EUID" -ne 0 ] && { echo "Error: Run as root"; exit 1; }
}
check_ec() {
[ ! -c "$EC_DEVICE" ] && { echo "Error: $EC_DEVICE not found"; exit 1; }
}
ec_read() {
dd if="$EC_DEVICE" bs=1 count=1 skip="$1" 2>/dev/null | od -An -tu1 | tr -d ' '
}
ec_write() {
local offset=$1 value=$2
[ "$value" -lt 0 ] || [ "$value" -gt 255 ] && return 1
printf '%02x' "$value" | xxd -r -p | dd of="$EC_DEVICE" bs=1 count=1 seek="$offset" conv=notrunc 2>/dev/null
sleep 0.02
[ "$(ec_read $offset)" -ne "$value" ] && return 1
return 0
}
get_status() {
local cpu_temp=$(ec_read $REG_CPU_TEMP)
local cpu_fan=$(ec_read $REG_CPU_FAN)
local gpu_temp=$(ec_read $REG_GPU_TEMP)
local gpu_fan=$(ec_read $REG_GPU_FAN)
local fan_mode=$(ec_read $REG_FAN_MODE)
local boost=$(ec_read $REG_COOLER_BOOST)
echo "MSI Fan Status"
echo "=============="
echo "CPU: ${cpu_temp}C @ ${cpu_fan}%"
echo "GPU: ${gpu_temp}C @ ${gpu_fan}%"
case $fan_mode in
140) echo "Mode: Advanced" ;;
76) echo "Mode: Basic" ;;
*) echo "Mode: $fan_mode" ;;
esac
[ "$boost" -ge 128 ] && echo "Cooler Boost: ON" || echo "Cooler Boost: OFF"
}
show_curve() {
echo "CPU Curve:"
printf " Temp: "
for reg in "${CPU_TEMP_REGS[@]}"; do printf "%3dC " $(ec_read $reg); done
echo ""
printf " Fan: "
for reg in "${CPU_FAN_REGS[@]}"; do printf "%3d%% " $(ec_read $reg); done
echo ""
echo "GPU Curve:"
printf " Temp: "
for reg in "${GPU_TEMP_REGS[@]}"; do printf "%3dC " $(ec_read $reg); done
echo ""
printf " Fan: "
for reg in "${GPU_FAN_REGS[@]}"; do printf "%3d%% " $(ec_read $reg); done
echo ""
}
backup_curve() {
local file=${1:-"$BACKUP_DIR/curve_$(date +%Y%m%d_%H%M%S).txt"}
mkdir -p "$BACKUP_DIR"
{
echo "# Fan curve backup - $(date)"
echo "FAN_MODE=$(ec_read $REG_FAN_MODE)"
for i in ${!CPU_TEMP_REGS[@]}; do echo "CPU_TEMP_$i=$(ec_read ${CPU_TEMP_REGS[$i]})"; done
for i in ${!CPU_FAN_REGS[@]}; do echo "CPU_FAN_$i=$(ec_read ${CPU_FAN_REGS[$i]})"; done
for i in ${!GPU_TEMP_REGS[@]}; do echo "GPU_TEMP_$i=$(ec_read ${GPU_TEMP_REGS[$i]})"; done
for i in ${!GPU_FAN_REGS[@]}; do echo "GPU_FAN_$i=$(ec_read ${GPU_FAN_REGS[$i]})"; done
} > "$file"
echo "Saved: $file"
}
restore_curve() {
[ ! -f "$1" ] && { echo "Error: File not found: $1"; return 1; }
source "$1"
ec_write $REG_FAN_MODE $FAN_MODE
for i in ${!CPU_TEMP_REGS[@]}; do eval "ec_write ${CPU_TEMP_REGS[$i]} \$CPU_TEMP_$i"; done
for i in ${!CPU_FAN_REGS[@]}; do eval "ec_write ${CPU_FAN_REGS[$i]} \$CPU_FAN_$i"; done
for i in ${!GPU_TEMP_REGS[@]}; do eval "ec_write ${GPU_TEMP_REGS[$i]} \$GPU_TEMP_$i"; done
for i in ${!GPU_FAN_REGS[@]}; do eval "ec_write ${GPU_FAN_REGS[$i]} \$GPU_FAN_$i"; done
echo "Restored from: $1"
}
apply_profile() {
local profile=$1
[ -z "${PROFILES[${profile}_cpu]}" ] && { echo "Unknown profile: $profile"; return 1; }
echo "Applying $profile profile..."
ec_write $REG_FAN_MODE 140
local cpu_profile=${PROFILES[${profile}_cpu]}
IFS='|' read -r cpu_temps cpu_fans <<< "$cpu_profile"
IFS=',' read -ra temps <<< "$cpu_temps"
IFS=',' read -ra fans <<< "$cpu_fans"
for i in ${!temps[@]}; do ec_write ${CPU_TEMP_REGS[$i]} ${temps[$i]}; done
for i in ${!fans[@]}; do ec_write ${CPU_FAN_REGS[$i]} ${fans[$i]}; done
local gpu_profile=${PROFILES[${profile}_gpu]}
IFS='|' read -r gpu_temps gpu_fans <<< "$gpu_profile"
IFS=',' read -ra temps <<< "$gpu_temps"
IFS=',' read -ra fans <<< "$gpu_fans"
for i in ${!temps[@]}; do ec_write ${GPU_TEMP_REGS[$i]} ${temps[$i]}; done
for i in ${!fans[@]}; do ec_write ${GPU_FAN_REGS[$i]} ${fans[$i]}; done
echo "Done"
show_curve
}
monitor() {
local interval=${1:-2}
while true; do
clear
get_status
echo ""
show_curve
sleep $interval
done
}
usage() {
cat << EOF
Usage: $0 <command> [args]
Commands:
status Show temps and fan speeds
curve Show current fan curve
apply <profile> Apply profile (silent|balanced|performance|gaming)
backup [file] Backup current curve
restore <file> Restore curve from backup
monitor [interval] Live monitoring
Examples:
sudo $0 status
sudo $0 apply performance
EOF
}
check_root
check_ec
case "$1" in
status) get_status ;;
curve) show_curve ;;
apply) apply_profile "$2" ;;
backup) backup_curve "$2" ;;
restore) restore_curve "$2" ;;
monitor) monitor "$2" ;;
*) usage; exit 1 ;;
esac