Codebase list mbpfan / ae28e89
New upstream version 2.1.1 Chow Loong Jin 4 years ago
16 changed file(s) with 484 addition(s) and 267 deletion(s). Raw diff Collapse all Expand all
0 dist: xenial
1 language: c
2 script:
3 - make
44
55 Marji Cermak <marji AT morpht DOT com>
66 John Ferlito <john AT inodes DOT org>
7 Andrew Gaul <gaul AT gaul DOT org>
78 Ismail Khatib <ikhatib AT gmail DOT com>
89 Trevor Joynson
910 Magnus Stubman <magnus AT stubman DOT eu>
1213 Yi Yang <ahyangyi AT gmail DOT com>
1314 Herminio Hernandez Jr <herminio.hernandezjr AT gmail DOT com>
1415 Robert Musial <rmusial AT fastmail DOT com>
16 Ati Sharma <ati.sharma AT gmail DOT com>
1517
1618
1719 ORIGINARY AUTHORS
99 MAN = mbpfan.8.gz
1010
1111 COPT =
12 CC = cc
12 CC ?= cc
1313 OBJFLAG = -o
1414 BINFLAG = -o
1515 INCLUDES =
00 mbpfan
11 ====================
22
3 [![Build Status](https://travis-ci.org/dgraziotin/mbpfan.svg?branch=master)](https://travis-ci.org/dgraziotin/mbpfan)
4
35 This is an enhanced version of [Allan McRae mbpfan](http://allanmcrae.com/2010/05/simple-macbook-pro-fan-daemon/)
46
57 mbpfan is a daemon that uses input from coretemp module and sets the fan speed using the applesmc module.
68 This enhanced version assumes any number of processors and fans (max. 10).
79
8 * It only uses the temperatures from the processors as input.
9 * It requires coretemp and applesmc kernel modules to be loaded.
10 * It requires root use
11 * It daemonizes or stays in foreground
12 * Verbose mode for both syslog and stdout
13 * Users can configure it using the file /etc/mbpfan.conf
10 * It only uses the temperatures from the processors as input.
11 * It requires coretemp and applesmc kernel modules to be loaded.
12 * It requires root use
13 * It daemonizes or stays in foreground
14 * Verbose mode for both syslog and stdout
15 * Users can configure it using the file /etc/mbpfan.conf
1416
1517 **Table Of Contents**
1618
1719 - [Supported GNU/Linux Distributions](#supported-gnulinux-distributions)
18 - [Tested Macbook Models](#tested-macbook-models)
19 - [Tested iMac/Mac Mini Models](#tested-imacmac-mini-models)
20 - [Tested MacBook Models](#tested-macbook-models)
21 - [Tested iMac/Mac mini Models](#tested-imacmac-mini-models)
2022 - [Requirements](#requirements)
2123 - [Installation](#installation)
2224 - [Arch Linux](#arch-linux)
4749 - Trisquel
4850 - Solus
4951
50 ## Tested Macbook Models
52 ## Tested MacBook Models
5153
5254 This section reports those models where mbpfan was tested successfully. It does not necessarily mean that the daemon does not work on non-listed models.
5355
6163 - MacBook Pro 6,2 15" (Intel i7 - Linux 3.5.0)
6264 - MacBook Pro 6,2 15" (Intel i7 - Linux 3.2.0)
6365 - MacBook Pro 2,2 15" (Intel Core 2 Duo - Linux 3.4.4)
66 - MacBook Air 6,1 13" (Intel i7 - Linux 3.13)
6467 - MacBook Air 5,2 13" (Intel i5 - Linux 3.16)
6568 - MacBook Air 1,1 13" (Intel Core Duo - Linux 4.4, Linux 4.8)
6669 - MacBook Air 7,2 13" (Intel Core Duo - Linux 4.10)
6770 - MacBook 1,1 (Intel Core Duo - Linux 3.16)
6871
69 ## Tested iMac/Mac Mini Models
70
71 This section reports the iMac/Mac-mini models where mbpfan was tested successfully.
72
73 - iMac Retina 16.2 21.5" (Intel i5 - Linux 4.4.0 Ubuntu 16.04)
72 ## Tested iMac/Mac mini Models
73
74 This section reports the iMac/Mac mini models where mbpfan was tested successfully.
75
76 - iMac Retina 16.2 21.5" (Intel i5 - Linux 4.4.0 Ubuntu 16.04)
7477 - iMac 12,1 21.5" (Intel i7 - Linux 4.6.4)
7578 - iMac 5,1 17" (Intel T7400 (Core 2 Duo) - Linux 14.04 Ubuntu)
76 - Mac Mini 2,1 (Core 2 Duo - Linux 4.4.0)
77 - Mac Mini 5,3 (Core i7 2.0 - Linux 4.4.0 elementary/Ubuntu)
78 - Mac Mini 6,1 (Core i7 2.3 - Linux 4.7.3-4-ck Archlinux)
79 - Mac mini 2,1 (Core 2 Duo - Linux 4.4.0)
80 - Mac mini 5,3 (Core i7 2.0 - Linux 4.4.0 elementary/Ubuntu)
81 - Mac mini 6,1 (Core i7 2.3 - Linux 4.7.3-4-ck Archlinux)
82
83 ## Tested Mac Pro Models
84
85 This section reports the Mac Pro models where mbpfan was tested successfully.
86
87 - Mac Pro 3,1
88 - Mac Pro 4,1 (with 5,1 firmware and hex-core Xeon X5690 mod - Linux 4.14)
7989
8090 ## Requirements
8191
8797
8898 In most distributions, you can run the following command:
8999
90 `lsmod | grep coretemp && lsmod | grep applesmc`
100 ```bash
101 lsmod | grep -e applesmc -e coretemp
102 ```
91103
92104 If you see `coretemp` and `applesmc` listed, you are all set.
93105
173185
174186 **Ubuntu**
175187
176 For systemd based init systems (Ubuntu 16.04+), the file mbpfan.service
177 has been provided.
178 For using it, execute:
179
180 sudo cp mbpfan.service /etc/systemd/system/
181 sudo systemctl enable mbpfan.service
182
188 For systemd based init systems (Ubuntu 16.04+), see the systemd section below.
183189
184190 For upstart based init systems (Ubuntu before 16.04), an example upstart job has been provided.
185191 For using it, execute:
221227 sudo systemctl daemon-reload
222228 sudo systemctl start mbpfan.service
223229
224 To start the service automatically at boot, also execute the following:
225
226 sudo systemctl enable mbpfan.service
227
228
229230 ## Usage
230231
231232 Usage: ./mbpfan OPTION(S)
253254
254255 **This Project uses following library:**
255256
256 * [ANSI C Application Settings Managment](http://pokristensson.com/settings.html) by Per Ola Kristensson.
257 * [ANSI C Application Settings Management](http://pokristensson.com/settings.html) by Per Ola Kristensson.
00 [general]
11 # see https://ineed.coffee/3838/a-beginners-tutorial-for-mbpfan-under-ubuntu for the values
2 min_fan_speed = 2000 # put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min"
3 max_fan_speed = 6200 # put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max"
2 #
3 # mbpfan will load the max / min speed of from the files produced by the applesmc driver. If these files are not found it will set all fans to the default of min_speed = 2000 and max_speed = 6200
4 # by setting the values for the speeds in this config it will override whatever it finds in:
5 # /sys/devices/platform/applesmc.768/fan*_min
6 # /sys/devices/platform/applesmc.768/fan*_max
7 # or the defaults.
8 #
9 # multiple fans can be configured by using the config key of min_fan*_speed and max_fan*_speed
10 # the number used will correlate to the file number of the fan in the applesmc driver that are used to control the fan speed.
11 #
12 #min_fan1_speed = 2000 # put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min"
13 #max_fan1_speed = 6200 # put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max"
414 low_temp = 63 # try ranges 55-63, default is 63
515 high_temp = 66 # try ranges 58-66, default is 66
616 max_temp = 86 # take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000
7 polling_interval = 7 # default is 7 seconds
17 polling_interval = 1 # default is 1 seconds
0 [general]
1 # see https://ineed.coffee/3838/a-beginners-tutorial-for-mbpfan-under-ubuntu for the values
2 min_fan1_speed = 2000 # put the *lowest* value of "cat /sys/devices/platform/applesmc.768/fan*_min"
3 max_fan1_speed = 6200 # put the *highest* value of "cat /sys/devices/platform/applesmc.768/fan*_max"
4 low_temp = 63 # try ranges 55-63, default is 63
5 high_temp = 66 # try ranges 58-66, default is 66
6 max_temp = 86 # take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000
7 polling_interval = 7 # default is 1 second
00 [general]
1 min_fan_speed = 6200 # default is 2000
2 max_fan_speed = 6200 # default is 6200
1 min_fan1_speed = 6200 # default is 2000
2 max_fan1_speed = 6200 # default is 6200
33 low_temp = 63 # try ranges 55-63, default is 63
44 high_temp = 66 # try ranges 58-66, default is 66
55 max_temp = 86 # do not set it > 90, default is 86
6 polling_interval = 1 # default is 7
6 polling_interval = 2 # default is 1 second
0 [general]
1 min_fan1_speed = 2000 # default is 2000
2 min_fan2_speed = 2000 # default is 6200
3 low_temp = 63 # try ranges 55-63, default is 63
4 high_temp = 66 # try ranges 58-66, default is 66
5 max_temp = 86 # do not set it > 90, default is 86
6 polling_interval = 1 # default is 1 second
0 Name: mbpfan
1 URL: https://github.com/dgraziotin/mbpfan
2 License: GPLv3
3 Group: System Environment/Daemons
4 Version: %{SOURCE_VERSION}
5 Release: 3
6 Summary: A simple daemon to control fan speed on all MacBook/MacBook Pros (probably all Apple computers) for Linux 3.x.x and 4.x.x
7 Source: v%{version}.tar.gz
8
9 %description
10 This is an enhanced version of Allan McRae mbpfan
11
12 mbpfan is a daemon that uses input from coretemp module and sets the fan speed using the applesmc module. This enhanced version assumes any number of processors and fans (max. 10).
13
14 It only uses the temperatures from the processors as input.
15 It requires coretemp and applesmc kernel modules to be loaded.
16 It requires root use
17 It daemonizes or stays in foreground
18 Verbose mode for both syslog and stdout
19 Users can configure it using the file /etc/mbpfan.conf
20
21 %prep
22 %setup -q -n %{name}-%{version}
23
24 %build
25 make
26
27 %install
28 install -D -m755 bin/mbpfan $RPM_BUILD_ROOT/usr/sbin/mbpfan
29 install -D -m644 mbpfan.conf $RPM_BUILD_ROOT/etc/mbpfan.conf
30 install -D -m644 mbpfan.service $RPM_BUILD_ROOT/usr/lib/systemd/system/mbpfan.service
31
32 %clean
33 rm -rf $RPM_BUILD_ROOT
34
35 %post
36 %systemd_post mbpfan.service
37 echo "mbpfan will auto detect sane values for min and max fan speeds."
38 echo "If you want to customize these values please edit:"
39 echo "/etc/mbpfan.conf"
40 echo "To start the daemon now type:"
41 echo "systemctl start mbpfan"
42 echo "To run also at boot, type:"
43 echo "systemctl enable mbpfan"
44
45 %preun
46 %systemd_preun mbpfan.service
47
48 %postun
49 %systemd_postun_with_restart mbpfan.service
50
51 %files
52 %defattr (-,root,root)
53 %doc AUTHORS README.md
54 /usr/sbin/mbpfan
55 %config /etc/mbpfan.conf
56 /usr/lib/systemd/system/mbpfan.service
57
58 %changelog
59 * Mon Sep 10 2018 Michele Codutti <codutti@gmail.com> - 2.0.2-3
60 - Removed autoconfig with suggested procedure because has been integrated on mbpfan.
61
62 * Sun Aug 19 2018 Michele Codutti <codutti@gmail.com> - 2.0.2-2
63 - Autoconfig with suggested procedure.
64 - Initial packaging
1616 */
1717
1818
19 #include <sys/prctl.h>
1920 #include <sys/types.h>
2021 #include <sys/stat.h>
2122 #include <stdio.h>
110111 switch(signal) {
111112 case SIGHUP:
112113 syslog(LOG_WARNING, "Received SIGHUP signal.");
113 retrieve_settings(NULL);
114 retrieve_settings(NULL, fans);
114115 break;
115116
116117 case SIGTERM:
117118 syslog(LOG_WARNING, "Received SIGTERM signal.");
118119 cleanup_and_exit(EXIT_SUCCESS);
120 break;
119121
120122 case SIGQUIT:
121123 syslog(LOG_WARNING, "Received SIGQUIT signal.");
122124 cleanup_and_exit(EXIT_SUCCESS);
125 break;
123126
124127 case SIGINT:
125128 syslog(LOG_WARNING, "Received SIGINT signal.");
126129 cleanup_and_exit(EXIT_SUCCESS);
130 break;
127131
128132 default:
129133 syslog(LOG_WARNING, "Unhandled signal (%d) %s", signal, strsignal(signal));
152156 openlog(PROGRAM_NAME, LOG_CONS, LOG_USER);
153157 }
154158
159 // configure timer slack
160 int err = prctl(PR_SET_TIMERSLACK, 1000 * 1000 * 1000, 0, 0, 0);
161 if (err == -1) {
162 perror("prctl");
163 }
155164
156165 pid_t pid_slave;
157166 pid_t sid_slave;
1818 char* path; // TODO: unused
1919 char* fan_output_path;
2020 char* fan_manual_path;
21 int step_up;
22 int step_down;
23 int fan_id;
24 int old_speed;
25 int fan_max_speed;
26 int fan_min_speed;
2127 struct s_fans *next;
2228 };
2329
2733 extern t_sensors* sensors;
2834 extern t_fans* fans;
2935
30 #endif
36 #endif
2828 #include "mbpfan.h"
2929 #include "daemon.h"
3030 #include "global.h"
31 #include "main.h"
3132 #include "minunit.h"
3233
3334 int daemonize = 1;
5152 printf("\n");
5253 }
5354 }
54
5555
5656 void check_requirements()
5757 {
9595
9696 }
9797
98
9998 int main(int argc, char *argv[])
10099 {
101100
113112 break;
114113
115114 case 't':
116 tests();
117 exit(EXIT_SUCCESS);
118 break;
115 return tests();
119116
120117 case 'v':
121118 verbose = 1;
128125 }
129126 }
130127
131
132
133128 check_requirements();
134129
135130 // pointer to mbpfan() function in mbpfan.c
0 void check_requirements();
44 * Modifications (2012) by Ismail Khatib <ikhatib@gmail.com>
55 * Modifications (2012-present) by Daniel Graziotin <daniel@ineed.coffee> [CURRENT MAINTAINER]
66 * Modifications (2017-present) by Robert Musial <rmusial@fastmail.com>
7 * Modifications (2018-present) by Ati Sharma <ati.sharma@gmail.com>
78 *
89 * This program is free software: you can redistribute it and/or modify
910 * it under the terms of the GNU General Public License as published by
1718 *
1819 *
1920 * Notes:
20 * Assumes any number of processors and fans (max. 10)
21 * Assumes any number of processors, cores and fans (max. 6, 16, 12
22 * as defined in NUM_PROCESSORS, NUM_HWMONS, NUM_TEMP_INPUTS and NUM_FANS)
2123 * It uses only the temperatures from the processors as input.
2224 * Requires coretemp and applesmc kernel modules to be loaded.
2325 * Requires root use
2527 * Tested models: see README.md
2628 */
2729
28
2930 #include <stdarg.h>
3031 #include <stdio.h>
3132 #include <stdlib.h>
3233 #include <unistd.h>
3334 #include <string.h>
35 #include <time.h>
3436 #include <math.h>
3537 #include <syslog.h>
3638 #include <stdbool.h>
4446 #define min(a,b) ((a) < (b) ? (a) : (b))
4547 #define max(a,b) ((a) > (b) ? (a) : (b))
4648
47 int min_fan_speed = 2000;
48 int max_fan_speed = 6200;
49
5049 /* temperature thresholds
5150 * low_temp - temperature below which fan speed will be at minimum
5251 * high_temp - fan will increase speed when higher than this temperature
5554 int high_temp = 66; // try ranges 58-66
5655 int max_temp = 86; // do not set it > 90
5756
58 int polling_interval = 7;
57 // maximum number of processors etc supported
58 #define NUM_PROCESSORS 6
59 #define NUM_HWMONS 12
60 #define NUM_TEMP_INPUTS 16
61 #define NUM_FANS 10
62 // sane defaults when user provides unexpected values
63 #define MIN_FAN_SPEED_DEFAULT 500
64 #define MAX_FAN_SPEED_DEFAULT 6500
65
66 int polling_interval = 1;
5967
6068 t_sensors* sensors = NULL;
6169 t_fans* fans = NULL;
6270
63
64 static char *smprintf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
65 static char *smprintf(const char *fmt, ...)
71 char *smprintf(const char *fmt, ...)
6672 {
6773 char *buf;
6874 int cnt;
8490 return buf;
8591 }
8692
87 bool is_legacy_sensors_path()
93 bool is_modern_sensors_path()
8894 {
8995 struct utsname kernel;
9096 uname(&kernel);
98104 exit(EXIT_FAILURE);
99105 }
100106
101
102 // thanks http://stackoverflow.com/questions/18192998/plain-c-opening-a-directory-with-fopen
103 fopen("/sys/devices/platform/coretemp.0/hwmon", "wb");
104
105 if (errno == EISDIR) {
106 return 0;
107 } else {
108 return 1;
109 }
110
111 //
112 // str_kernel_version = strtok(NULL, ".");
113 // int kernel_version = atoi(str_kernel_version);
114
115 // if(verbose) {
116 // printf("Detected kernel version: %s\n", kernel.release);
117 // printf("Detected kernel minor revision: %s\n", str_kernel_version);
118
119 // if(daemonize) {
120 // syslog(LOG_INFO, "Kernel version: %s", kernel.release);
121 // syslog(LOG_INFO, "Detected kernel minor revision: %s", str_kernel_version);
122 // }
123 // }
124
125 // return (atoi(kernel.release) == 3 && kernel_version < 15);
107 int counter;
108
109 for (counter = 0; counter < NUM_HWMONS; counter++) {
110 int temp;
111 for (temp = 1; temp < NUM_TEMP_INPUTS; ++temp) {
112 char *path = smprintf("/sys/devices/platform/coretemp.0/hwmon/hwmon%d/temp%d_input", counter, temp);
113 int res = access(path, R_OK);
114 free(path);
115 if (res == 0) {
116 return 1;
117 }
118 }
119 }
120
121 return 0;
126122 }
127123
128124
129125 t_sensors *retrieve_sensors()
130126 {
131
132127 t_sensors *sensors_head = NULL;
133128 t_sensors *s = NULL;
134129
135130 char *path = NULL;
136131 char *path_begin = NULL;
137132
138 if (is_legacy_sensors_path()) {
133 const char *path_end = "_input";
134 int sensors_found = 0;
135
136 if (!is_modern_sensors_path()) {
139137 if(verbose) {
140138 printf("Using legacy sensor path for kernel < 3.15.0\n");
141139
156154 }
157155 }
158156
159 path_begin = strdup("/sys/devices/platform/coretemp.0/hwmon/hwmon");
160
161 int counter;
162 for (counter = 0; counter < 10; counter++) {
163
164 char hwmon_path[strlen(path_begin)+2];
165
166 sprintf(hwmon_path, "%s%d", path_begin, counter);
167
168 // thanks http://stackoverflow.com/questions/18192998/plain-c-opening-a-directory-with-fopen
169 fopen(hwmon_path, "wb");
170
171 if (errno == EISDIR) {
172
173 path_begin = smprintf("%s/temp", hwmon_path);
174
175 if(verbose) {
176 printf("Found hwmon path at %s\n", path_begin);
177
178 if(daemonize) {
179 syslog(LOG_INFO, "Found hwmon path at %s\n", path_begin);
180 }
181
182 }
183
184 break;
185 }
186 }
187 }
188
189 const char *path_end = "_input";
190
191 int sensors_found = 0;
192
193 int counter = 0;
194 for(counter = 0; counter<10; counter++) {
195 path = smprintf("%s%d%s", path_begin, counter, path_end);
196
197 FILE *file = fopen(path, "r");
198
199 if(file != NULL) {
200 s = (t_sensors *) malloc( sizeof( t_sensors ) );
201 s->path = strdup(path);
202 fscanf(file, "%d", &s->temperature);
203
204 if (sensors_head == NULL) {
205 sensors_head = s;
206 sensors_head->next = NULL;
207
208 } else {
209 t_sensors *tmp = sensors_head;
210
211 while (tmp->next != NULL) {
212 tmp = tmp->next;
213 }
214
215 tmp->next = s;
216 tmp->next->next = NULL;
217 }
218
219 s->file = file;
220 sensors_found++;
221 }
222
223 free(path);
224 path = NULL;
157 // loop over up to 6 processors
158 int processor;
159 for (processor = 0; processor < NUM_PROCESSORS; processor++) {
160
161 if (path_begin != NULL) {
162 free(path_begin);
163 }
164 path_begin = smprintf("/sys/devices/platform/coretemp.%d/hwmon/hwmon", processor);
165
166 int counter;
167 for (counter = 0; counter < NUM_HWMONS; counter++) {
168
169 char hwmon_path[strlen(path_begin)+2];
170
171 sprintf(hwmon_path, "%s%d", path_begin, counter);
172
173 int res = access(hwmon_path, R_OK);
174 if (res == 0) {
175
176 free(path_begin);
177 path_begin = smprintf("%s/temp", hwmon_path);
178
179 if(verbose) {
180 printf("Found hwmon path at %s\n", path_begin);
181
182 if(daemonize) {
183 syslog(LOG_INFO, "Found hwmon path at %s\n", path_begin);
184 }
185
186 }
187
188 break;
189 }
190 }
191
192 int core = 0;
193 for(core = 0; core<NUM_TEMP_INPUTS; core++) {
194 path = smprintf("%s%d%s", path_begin, core, path_end);
195
196 FILE *file = fopen(path, "r");
197
198 if(file != NULL) {
199 s = (t_sensors *) malloc( sizeof( t_sensors ) );
200 s->path = strdup(path);
201 fscanf(file, "%d", &s->temperature);
202
203 if (sensors_head == NULL) {
204 sensors_head = s;
205 sensors_head->next = NULL;
206
207 } else {
208 t_sensors *tmp = sensors_head;
209
210 while (tmp->next != NULL) {
211 tmp = tmp->next;
212 }
213
214 tmp->next = s;
215 tmp->next->next = NULL;
216 }
217
218 s->file = file;
219 sensors_found++;
220 }
221
222 free(path);
223 path = NULL;
224 }
225 }
225226 }
226227
227228 if(verbose) {
244245 return sensors_head;
245246 }
246247
248 static int read_value(const char *path)
249 {
250 int value = -1;
251 FILE *file = fopen(path, "r");
252 if (file != NULL) {
253 fscanf(file, "%d", &value);
254 fclose(file);
255 }
256 return value;
257 }
247258
248259 t_fans *retrieve_fans()
249260 {
250
251261 t_fans *fans_head = NULL;
252262 t_fans *fan = NULL;
253263
254264 char *path_output = NULL;
255265 char *path_manual = NULL;
266 char *path_fan_max = NULL;
267 char *path_fan_min = NULL;
256268
257269 const char *path_begin = "/sys/devices/platform/applesmc.768/fan";
258270 const char *path_output_end = "_output";
259271 const char *path_man_end = "_manual";
260
272 const char *path_max_speed = "_max";
273 const char *path_min_speed = "_min";
274
261275 int counter = 0;
262276 int fans_found = 0;
263277
264 for(counter = 0; counter<10; counter++) {
278 for(counter = 0; counter<NUM_FANS; counter++) {
265279
266280 path_output = smprintf("%s%d%s", path_begin, counter, path_output_end);
267281 path_manual = smprintf("%s%d%s", path_begin, counter, path_man_end);
282 path_fan_min = smprintf("%s%d%s",path_begin, counter, path_min_speed);
283 path_fan_max = smprintf("%s%d%s",path_begin, counter, path_max_speed);
268284
269285 FILE *file = fopen(path_output, "w");
270286
272288 fan = (t_fans *) malloc( sizeof( t_fans ) );
273289 fan->fan_output_path = strdup(path_output);
274290 fan->fan_manual_path = strdup(path_manual);
291 fan->fan_id = counter;
292
293 int fan_speed = read_value(path_fan_min);
294 if(fan_speed == -1 || fan_speed < MIN_FAN_SPEED_DEFAULT)
295 fan->fan_min_speed = MIN_FAN_SPEED_DEFAULT;
296 else
297 fan->fan_min_speed = fan_speed;
298
299 fan_speed = read_value(path_fan_max);
300 if(fan_speed == -1 || fan_speed > MAX_FAN_SPEED_DEFAULT)
301 fan->fan_max_speed = MAX_FAN_SPEED_DEFAULT;
302 else
303 fan->fan_max_speed = fan_speed;
304
305 fan->old_speed = 0;
275306
276307 if (fans_head == NULL) {
277308 fans_head = fan;
291322 fan->file = file;
292323 fans_found++;
293324 }
294
325 free(path_fan_min);
326 path_fan_min = NULL;
327 free(path_fan_max);
328 path_fan_max = NULL;
295329 free(path_output);
296330 path_output = NULL;
297331 free(path_manual);
306340 }
307341 }
308342
309 if (!fans_found > 0){
343 if (fans_found == 0){
310344 syslog(LOG_CRIT, "mbpfan could not detect any fan. Please contact the developer.\n");
311345 printf("mbpfan could not detect any fan. Please contact the developer.\n");
312346 exit(EXIT_FAILURE);
313347 }
314348
315
316349 return fans_head;
317350 }
318351
319
320352 static void set_fans_mode(t_fans *fans, int mode)
321353 {
322
323354 t_fans *tmp = fans;
324355 FILE *file;
325356
349380
350381 t_sensors *refresh_sensors(t_sensors *sensors)
351382 {
352
353383 t_sensors *tmp = sensors;
354384
355385 while(tmp != NULL) {
366396 return sensors;
367397 }
368398
369
370 /* Controls the speed of the fan */
371 void set_fan_speed(t_fans* fans, int speed)
372 {
373 t_fans *tmp = fans;
374
375 while(tmp != NULL) {
376 if(tmp->file != NULL) {
377 char buf[16];
378 int len = snprintf(buf, sizeof(buf), "%d", speed);
379 pwrite(fileno(tmp->file), buf, len, /*offset=*/ 0);
380 }
381
382 tmp = tmp->next;
383 }
384
385 }
386
387
399 /* Controls the speed of a fan */
400 void set_fan_speed(t_fans* fan, int speed)
401 {
402 if(fan != NULL && fan->file != NULL && fan->old_speed != speed) {
403 char buf[16];
404 int len = snprintf(buf, sizeof(buf), "%d", speed);
405 int res = pwrite(fileno(fan->file), buf, len, /*offset=*/ 0);
406 if (res == -1) {
407 perror("Could not set fan speed");
408 }
409 fan->old_speed = speed;
410 }
411 }
412
413 void set_fan_minimum_speed(t_fans* fans)
414 {
415 t_fans *tmp = fans;
416
417 while(tmp != NULL) {
418 set_fan_speed(tmp,tmp->fan_min_speed);
419 tmp = tmp->next;
420 }
421 }
388422 unsigned short get_temp(t_sensors* sensors)
389423 {
390424 sensors = refresh_sensors(sensors);
410444 return temp;
411445 }
412446
413
414 void retrieve_settings(const char* settings_path)
447 void retrieve_settings(const char* settings_path, t_fans* fans)
415448 {
416449 Settings *settings = NULL;
417450 int result = 0;
450483 }
451484
452485 } else {
453 /* Read configfile values */
454 result = settings_get_int(settings, "general", "min_fan_speed");
455
456 if (result != 0) {
457 min_fan_speed = result;
458 }
459
460 result = settings_get_int(settings, "general", "max_fan_speed");
461
462 if (result != 0) {
463 max_fan_speed = result;
464 }
465
486
487 t_fans *fan = fans;
488
489 while(fan != NULL) {
490
491 char* config_key;
492 config_key = smprintf("min_fan%d_speed", fan->fan_id);
493 /* Read configfile values */
494 result = settings_get_int(settings, "general", config_key);
495 if (result != 0) {
496 fan->fan_min_speed = result;
497 }
498 free(config_key);
499
500 config_key = smprintf("max_fan%d_speed", fan->fan_id);
501 result = settings_get_int(settings, "general", config_key);
502
503 if (result != 0) {
504 fan->fan_max_speed = result;
505 }
506 free(config_key);
507 fan = fan->next;
508 }
466509 result = settings_get_int(settings, "general", "low_temp");
467510
468511 if (result != 0) {
493536 }
494537 }
495538
496
497539 void mbpfan()
498540 {
499541 int old_temp, new_temp, fan_speed, steps;
500542 int temp_change;
501 int step_up, step_down;
502
503 retrieve_settings(NULL);
504
543
505544 sensors = retrieve_sensors();
506545 fans = retrieve_fans();
546
547 retrieve_settings(NULL, fans);
548
549 t_fans* fan = fans;
550 while(fan != NULL) {
551
552 if (fan->fan_min_speed > fan->fan_max_speed) {
553 syslog(LOG_INFO, "Invalid fan speeds: %d %d", fan->fan_min_speed, fan->fan_max_speed);
554 printf("Invalid fan speeds: %d %d\n", fan->fan_min_speed, fan->fan_max_speed);
555 exit(EXIT_FAILURE);
556 }
557 fan = fan->next;
558 }
559
560 if (low_temp > high_temp || high_temp > max_temp) {
561 syslog(LOG_INFO, "Invalid temperatures: %d %d %d", low_temp, high_temp, max_temp);
562 printf("Invalid temperatures: %d %d %d\n", low_temp, high_temp, max_temp);
563 exit(EXIT_FAILURE);
564 }
565
507566 set_fans_man(fans);
508567
509568 new_temp = get_temp(sensors);
510
511 fan_speed = min_fan_speed;
512 set_fan_speed(fans, fan_speed);
569 set_fan_minimum_speed(fans);
570
571 fan = fans;
572 while(fan != NULL) {
573
574 fan->step_up = (float)( fan->fan_max_speed - fan->fan_min_speed ) /
575 (float)( ( max_temp - high_temp ) * ( max_temp - high_temp + 1 ) / 2 );
576
577 fan->step_down = (float)( fan->fan_max_speed - fan->fan_min_speed ) /
578 (float)( ( max_temp - low_temp ) * ( max_temp - low_temp + 1 ) / 2 );
579 fan = fan->next;
580 }
513581
514582 if(verbose) {
515583 printf("Sleeping for 2 seconds to get first temp delta\n");
519587 }
520588 }
521589 sleep(2);
522
523 step_up = (float)( max_fan_speed - min_fan_speed ) /
524 (float)( ( max_temp - high_temp ) * ( max_temp - high_temp + 1 ) / 2 );
525
526 step_down = (float)( max_fan_speed - min_fan_speed ) /
527 (float)( ( max_temp - low_temp ) * ( max_temp - low_temp + 1 ) / 2 );
528590
529591 while(1) {
530592 old_temp = new_temp;
531593 new_temp = get_temp(sensors);
532594
533 if(new_temp >= max_temp && fan_speed != max_fan_speed) {
534 fan_speed = max_fan_speed;
535 }
536
537 if(new_temp <= low_temp && fan_speed != min_fan_speed) {
538 fan_speed = min_fan_speed;
539 }
540
541 temp_change = new_temp - old_temp;
542
543 if(temp_change > 0 && new_temp > high_temp && new_temp < max_temp) {
544 steps = ( new_temp - high_temp ) * ( new_temp - high_temp + 1 ) / 2;
545 fan_speed = max( fan_speed, ceil(min_fan_speed + steps * step_up) );
546 }
547
548 if(temp_change < 0 && new_temp > low_temp && new_temp < max_temp) {
549 steps = ( max_temp - new_temp ) * ( max_temp - new_temp + 1 ) / 2;
550 fan_speed = min( fan_speed, floor(max_fan_speed - steps * step_down) );
551 }
552
553 if(verbose) {
554 printf("Old Temp %d: New Temp: %d, Fan Speed: %d\n", old_temp, new_temp, fan_speed);
555
556 if(daemonize) {
557 syslog(LOG_INFO, "Old Temp %d: New Temp: %d, Fan Speed: %d", old_temp, new_temp, fan_speed);
558 }
559 }
560
561 set_fan_speed(fans, fan_speed);
595 fan = fans;
596
597 while(fan != NULL) {
598 fan_speed = fan->old_speed;
599
600 if(new_temp >= max_temp && fan->old_speed != fan->fan_max_speed) {
601 fan_speed = fan->fan_max_speed;
602 }
603
604 if(new_temp <= low_temp && fan_speed != fan->fan_min_speed) {
605 fan_speed = fan->fan_min_speed;
606 }
607
608 temp_change = new_temp - old_temp;
609
610 if(temp_change > 0 && new_temp > high_temp && new_temp < max_temp) {
611 steps = ( new_temp - high_temp ) * ( new_temp - high_temp + 1 ) / 2;
612 fan_speed = max( fan_speed, ceil(fan->fan_min_speed + steps * fan->step_up) );
613 }
614
615 if(temp_change < 0 && new_temp > low_temp && new_temp < max_temp) {
616 steps = ( max_temp - new_temp ) * ( max_temp - new_temp + 1 ) / 2;
617 fan_speed = min( fan_speed, floor(fan->fan_max_speed - steps * fan->step_down) );
618 }
619
620 if(verbose) {
621 printf("Old Temp %d: New Temp: %d, Fan Speed: %d\n", old_temp, new_temp, fan_speed);
622
623 if(daemonize) {
624 syslog(LOG_INFO, "Old Temp %d: New Temp: %d, Fan Speed: %d", old_temp, new_temp, fan_speed);
625 }
626 }
627
628 set_fan_speed(fan, fan_speed);
629 fan = fan->next;
630 }
562631
563632 if(verbose) {
564633 printf("Sleeping for %d seconds\n", polling_interval);
568637 syslog(LOG_INFO, "Sleeping for %d seconds", polling_interval);
569638 }
570639 }
571
572 sleep(polling_interval);
573 }
574 }
640
641 // call nanosleep instead of sleep to avoid rt_sigprocmask and
642 // rt_sigaction
643 struct timespec ts;
644 ts.tv_sec = polling_interval;
645 ts.tv_nsec = 0;
646 nanosleep(&ts, NULL);
647 }
648 }
1515
1616 #ifndef _MBPFAN_H_
1717 #define _MBPFAN_H_
18
19 /** Basic fan speed parameters
20 */
21 extern int min_fan_speed;
22 extern int max_fan_speed;
2318
2419 /** Temperature Thresholds
2520 * low_temp - temperature below which fan speed will be at minimum
4237 struct s_fans;
4338 typedef struct s_fans t_fans;
4439
40 char *smprintf(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
41
4542 /**
4643 * Return true if the kernel is < 3.15.0
4744 */
5249 * /etc/mbpfan.conf
5350 * If it fails, the default hardcoded settings are used
5451 */
55 void retrieve_settings(const char* settings_path);
52 void retrieve_settings(const char* settings_path, t_fans *fans);
5653
5754 /**
5855 * Detect the sensors in /sys/devices/platform/coretemp.0/temp
56 * and /sys/devices/platform/coretemp.1/temp etc
5957 * Return a linked list of t_sensors (first temperature detected)
6058 */
6159 t_sensors *retrieve_sensors();
8583 void set_fans_auto(t_fans *fans);
8684
8785 /**
88 * Given a list of sensors with associated fans
86 * Given a sensors with associated fans
8987 * Change their speed
9088 */
91 void set_fan_speed(t_fans* fans, int speed);
89 void set_fan_speed(t_fans* fan, int speed);
9290
91 /**
92 * Given a list of fans set their minumum fan speed
93 */
94 void set_fan_minimum_speed(t_fans* fans);
9395 /**
9496 * Return average CPU temp in degrees (ceiling)
9597 */
100102 */
101103 void mbpfan();
102104
103 #endif
105 #endif
88 #include "global.h"
99 #include "mbpfan.h"
1010 #include "settings.h"
11 #include "main.h"
1112 #include "minunit.h"
1213
1314 int tests_run = 0;
115116 return 0;
116117 }
117118
118 mu_assert("Could not read min_fan_speed from config file",settings_get_int(settings, "general", "min_fan_speed") != 0);
119 mu_assert("Could not read max_fan_speed from config file",settings_get_int(settings, "general", "max_fan_speed") != 0);
120119 mu_assert("Could not read low_temp from config file",settings_get_int(settings, "general", "low_temp") != 0);
121120 mu_assert("Could not read high_temp from config file",settings_get_int(settings, "general", "high_temp") != 0);
122121 mu_assert("Could not read max_temp from config file",settings_get_int(settings, "general", "max_temp") != 0);
129128
130129 static const char *test_settings()
131130 {
132 retrieve_settings("./mbpfan.conf.test1");
133 mu_assert("max_fan_speed value is not 6200", max_fan_speed == 6200);
134 mu_assert("polling_interval is not 1", polling_interval == 1);
135 retrieve_settings("./mbpfan.conf");
136 mu_assert("min_fan_speed value is not 2000", min_fan_speed == 2000);
131 t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) );
132 fan->fan_id = 1;
133 fan->fan_max_speed = -1;
134 fan->next = NULL;
135
136 retrieve_settings("./mbpfan.conf.test1", fan);
137 mu_assert("max_fan_speed value is not 6200", fan->fan_max_speed == 6200);
138 mu_assert("polling_interval is not 2", polling_interval == 2);
139
140 fan->fan_min_speed = -1;
141 retrieve_settings("./mbpfan.conf.test0", fan);
142 mu_assert("min_fan_speed value is not 2000", fan->fan_min_speed == 2000);
137143 mu_assert("polling_interval is not 7", polling_interval == 7);
138 return 0;
139 }
140
144
145 t_fans* fan2 = (t_fans *)malloc(sizeof(t_fans));
146 fan2->fan_id = 2;
147 fan2->fan_max_speed = -1;
148 fan->next = fan2;
149
150 retrieve_settings("./mbpfan.conf.test2", fan);
151 mu_assert("min_fan1_speed value is not 2000", fan->fan_min_speed == 2000);
152 mu_assert("min_fan2_speed value is not 2000", fan->next->fan_min_speed == 2000);
153
154 free(fan2);
155 fan->next = NULL;
156 free(fan);
157
158 return 0;
159
160 }
141161 int received = 0;
142162
143163 static void handler(int signal)
144164 {
165 t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) );
166 fan->fan_id = 1;
167 fan->next = NULL;
168
145169
146170 switch(signal) {
147171 case SIGHUP:
148172 received = 1;
149 retrieve_settings("./mbpfan.conf.test1");
173 retrieve_settings("./mbpfan.conf.test1", fan);
174 free(fan);
150175 break;
151176
152177 default:
153178 received = 0;
179 free(fan);
154180 break;
155181 }
156182 }
165191
166192 static const char *test_settings_reload()
167193 {
194 t_fans* fan = (t_fans *) malloc( sizeof( t_fans ) );
195 fan->fan_id = 1;
196 fan->next = NULL;
197
168198 signal(SIGHUP, handler);
169 retrieve_settings("./mbpfan.conf");
199 retrieve_settings("./mbpfan.conf", fan);
170200 printf("Testing the _supplied_ mbpfan.conf (not the one you are using)..\n");
171 mu_assert("min_fan_speed value is not 2000 before SIGHUP", min_fan_speed == 2000);
172 mu_assert("polling_interval is not 7 before SIHUP", polling_interval == 7);
201 // cannot tests min_fan_speed since it is not set and thus auto-detected
202 mu_assert("polling_interval is not 1 before SIGHUP", polling_interval == 1);
173203 raise(SIGHUP);
174 mu_assert("min_fan_speed value is not 6200 after SIGHUP", min_fan_speed == 6200);
175 mu_assert("polling_interval is not 1 after SIHUP", polling_interval == 1);
176 retrieve_settings("./mbpfan.conf");
204 mu_assert("min_fan_speed value is not 6200 after SIGHUP", fan->fan_min_speed == 6200);
205 mu_assert("polling_interval is not 2 after SIGHUP", polling_interval == 2);
206 retrieve_settings("./mbpfan.conf", fan);
207 free(fan);
177208 return 0;
178209 }
179210
192223
193224 int tests()
194225 {
226 check_requirements();
227
195228 printf("Starting the tests..\n");
196229 printf("It is normal for them to take a bit to finish.\n");
197230
198231 const char *result = all_tests();
199232
200233 if (result != 0) {
201 printf("%s \n", result);
234 printf("Error: %s \n", result);
202235
203236 } else {
204237 printf("ALL TESTS PASSED\n");