From 8d89d05cf2460fad11b8e16dc4cdb503d80548b6 Mon Sep 17 00:00:00 2001 From: Daniel Graziotin Date: Sat, 9 Jun 2012 16:25:28 +0200 Subject: [PATCH] Initial commit of enhanced version --- Makefile | 56 ++++++++ README | 21 ++- bin/mbpfan | Bin 0 -> 20950 bytes mbpfan.c | 147 ------------------- mbpfan.rc => mbpfan.rc-BROKEN | 0 src/mbpfan.c | 261 ++++++++++++++++++++++++++++++++++ src/mbpfan.h | 41 ++++++ src/mbpfan.o | Bin 0 -> 19464 bytes 8 files changed, 378 insertions(+), 148 deletions(-) create mode 100644 Makefile create mode 100755 bin/mbpfan delete mode 100644 mbpfan.c rename mbpfan.rc => mbpfan.rc-BROKEN (100%) create mode 100644 src/mbpfan.c create mode 100644 src/mbpfan.h create mode 100644 src/mbpfan.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a6f096 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +COMPILER=G++ + +# todo: object files into output path, processing c / c++ files in the same time (?), nested directories for source files (?) +C = c +OUTPUT_PATH = bin/ +SOURCE_PATH = src/ +EXE = bin/mbpfan + +ifeq ($(COMPILER), G++) + ifeq ($(OS),Windows_NT) + OBJ = obj + else + OBJ = o + endif + COPT = -O2 + CCMD = g++ + OBJFLAG = -o + EXEFLAG = -o +# INCLUDES = -I../.includes + INCLUDES = +# LIBS = -lgc + LIBS = -lm +# LIBPATH = -L../gc/.libs + LIBPATH = + CPPFLAGS = $(COPT) -g $(INCLUDES) + LDFLAGS = $(LIBPATH) -g $(LIBS) + DEP = dep +else + OBJ = obj + COPT = /O2 + CCMD = cl + OBJFLAG = /Fo + EXEFLAG = /Fe +# INCLUDES = /I..\\.includes + INCLUDES = +# LIBS = ..\\.libs\\libgc.lib + LIBS = + CPPFLAGS = $(COPT) /DEBUG $(INCLUDES) + LDFLAGS = /DEBUG +endif + +OBJS := $(patsubst %.$(C),%.$(OBJ),$(wildcard $(SOURCE_PATH)*.$(C))) + +%.$(OBJ):%.$(C) + @echo Compiling $(basename $<)... + $(CCMD) -c $(CPPFLAGS) $(CXXFLAGS) $< $(OBJFLAG)$@ + +all: $(OBJS) + @echo Linking... + $(CCMD) $(LDFLAGS) $^ $(LIBS) $(EXEFLAG) $(EXE) + +clean: + rm -rf $(SOURCE_PATH)*.$(OBJ) $(EXE) + +rebuild: clean all +#rebuild is not entirely correct \ No newline at end of file diff --git a/README b/README index a85248e..a8a499e 100644 --- a/README +++ b/README @@ -1,4 +1,23 @@ -This is a daemon that uses input from coretemp module and sets the fan speed using the applesmc module. It includes a PKGBUILD for easy installation in Arch Linux. +This is an enhanced version of rvega's Fan-Control-Daemon , +which itself is an enhanced version of Allan McRae mbpfan + +Fan-Control-Daemon 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). + + * It only uses the temperatures from the processors as input. + * It requires coretemp and applesmc kernel modules to be loaded. + * It requires root use + + +Compile with +gcc -o mbpfan -lm mbpfan.c + +Run with +sudo ./mbpfan + +Original README below. +-- +It includes a PKGBUILD for easy installation in Arch Linux. Based on: diff --git a/bin/mbpfan b/bin/mbpfan new file mode 100755 index 0000000000000000000000000000000000000000..cb5a8c24479d128efefe60681ab0e32b82a2952b GIT binary patch literal 20950 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 literal 0 HcmV?d00001 diff --git a/mbpfan.c b/mbpfan.c deleted file mode 100644 index 7cf3844..0000000 --- a/mbpfan.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * mbpfan.c - automatically control fan for MacBook Pro - * Copyright (C) 2010 Allan McRae - * Modifications by Rafael Vega - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 20110811 - v1.1 - * - * Notes: - * Uses only the temperatures from the processors as input. - * Assumes two processors and one fan. - * Requires coretemp and applesmc kernel modules to be loaded. - * - * Tested models: - * MacBook Pro 8.1 13" (Intel i7 - Linux 3.2) - */ - - -#include -#include -#include - -/* lazy min/max... */ -#define min(a,b) a < b ? a : b -#define max(a,b) a > b ? a : b - -/* basic fan speed parameters */ -unsigned short min_fan_speed=2000; -unsigned short max_fan_speed=6200; - -/* temperature thresholds - * low_temp - temperature below which fan speed will be at minimum - * high_temp - fan will increase speed when higher than this temperature - * max_temp - fan will run at full speed above this temperature */ -unsigned short low_temp=63; -unsigned short high_temp=66; -unsigned short max_temp=86; -/* unsigned short low_temp=55; */ -/* unsigned short high_temp=65; */ -/* unsigned short max_temp=80; */ - -/* temperature polling interval */ -unsigned short polling_interval=10; - - -/* Controls the speed of the fan */ -void set_fan_speed(unsigned short speed) -{ - FILE *file; - - file=fopen("/sys/devices/platform/applesmc.768/fan1_min", "w"); - fprintf(file, "%d", speed); - fclose(file); -} - - -/* Takes "manual" control of fan */ -/* void prepare_fan() */ -/* { */ -/* FILE *file; */ -/* */ -/* file=fopen("/sys/devices/platform/applesmc.768/fan1_manual", "w"); */ -/* fprintf(file, "%d", 1); */ -/* fclose(file); */ -/* } */ - - -/* Returns average CPU temp in degrees (ceiling) */ -unsigned short get_temp() -{ - FILE *file; - unsigned short temp; - unsigned int t0, t1; - - file=fopen("/sys/devices/platform/coretemp.0/temp2_input", "r"); - fscanf(file, "%d", &t0); - fclose(file); - - file=fopen("/sys/devices/platform/coretemp.0/temp3_input", "r"); - fscanf(file, "%d", &t1); - fclose(file); - - temp = (unsigned short)(ceil((float)(t0 + t1) / 2000.)); - return temp; -} - - -int main() -{ - unsigned short old_temp, new_temp, fan_speed, steps; - short temp_change; - float step_up, step_down; - - /* prepare_fan(); */ - - /* assume running on boot so set fan speed to minimum */ - new_temp = get_temp(); - fan_speed = 2000; - set_fan_speed(fan_speed); - sleep(polling_interval); - - step_up = (float)(max_fan_speed - min_fan_speed) / - (float)((max_temp - high_temp) * (max_temp - high_temp + 1) / 2); - - step_down = (float)(max_fan_speed - min_fan_speed) / - (float)((max_temp - low_temp) * (max_temp - low_temp + 1) / 2); - - while(1) - { - old_temp = new_temp; - new_temp = get_temp(); - - if(new_temp >= max_temp && fan_speed != max_fan_speed) { - fan_speed = max_fan_speed; - } - - if(new_temp <= low_temp && fan_speed != min_fan_speed) { - fan_speed = min_fan_speed; - } - - temp_change = new_temp - old_temp; - - if(temp_change > 0 && new_temp > high_temp && new_temp < max_temp) { - steps = (new_temp - high_temp) * (new_temp - high_temp + 1) / 2; - fan_speed = max(fan_speed, ceil(min_fan_speed + steps * step_up)); - } - - if(temp_change < 0 && new_temp > low_temp && new_temp < max_temp) { - steps = (max_temp - new_temp) * (max_temp - new_temp + 1) / 2; - fan_speed = min(fan_speed, floor(max_fan_speed - steps * step_down)); - } - - set_fan_speed(fan_speed); - sleep(polling_interval); - } - - return 0; -} diff --git a/mbpfan.rc b/mbpfan.rc-BROKEN similarity index 100% rename from mbpfan.rc rename to mbpfan.rc-BROKEN diff --git a/src/mbpfan.c b/src/mbpfan.c new file mode 100644 index 0000000..5ca8931 --- /dev/null +++ b/src/mbpfan.c @@ -0,0 +1,261 @@ +/** + * mbpfan.c - automatically control fan for MacBook Pro + * Copyright (C) 2010 Allan McRae + * Modifications by Rafael Vega + * Modifications (2012) by Daniel Graziotin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 2012-06-09 - v1.2 + * + * Notes: + * Assumes any number of processors and fans (max. 10) + * It Uses only the temperatures from the processors as input. + * Requires coretemp and applesmc kernel modules to be loaded. + * Requires root use + * + * Tested models: + * MacBook Pro 8.1 13" (Intel i7 - Linux 3.2) + * Macbook Pro 6,2 15" (Intel i7 - Linux 3.2) + */ + + +#include +#include +#include +#include +#include +#include "mbpfan.h" + +/* lazy min/max... */ +#define min(a,b) a < b ? a : b +#define max(a,b) a > b ? a : b + +unsigned short min_fan_speed = 2000; +unsigned short max_fan_speed = 6200; + +/* temperature thresholds + * low_temp - temperature below which fan speed will be at minimum + * high_temp - fan will increase speed when higher than this temperature + * max_temp - fan will run at full speed above this temperature */ +unsigned short low_temp = 63; // try ranges 55-63 +unsigned short high_temp = 66; // try ranges 58-66 +unsigned short max_temp = 86; // do not set it > 90 + +unsigned short polling_interval = 7; + + +struct s_sensors { + char* path; + char* fan_path; + unsigned int temperature; + struct s_sensors *next; +}; + +void find_fans(t_sensors* sensors) +{ + t_sensors *tmp = sensors; + + char *path = NULL; + const char *path_begin = "/sys/devices/platform/applesmc.768/fan"; + const char *path_end = "_min"; + int path_size = strlen(path_begin) + strlen(path_end) + 2; + char number[1]; + sprintf(number,"%d",0); + + int counter = 0; + + for(counter = 0; counter<10; counter++) { + path = (char*) malloc(sizeof( char ) * path_size); + path[0] = '\0'; + sprintf(number,"%d",counter); + + strncat( path, path_begin, strlen(path_begin) ); + strncat( path, number, strlen(number) ); + strncat( path, path_end, strlen(path_begin) ); + + FILE *file = fopen(path, "r"); + + if(file != NULL) { + if (tmp->path != NULL) + tmp->fan_path = (char *) malloc(sizeof( char ) * path_size); + strcpy(tmp->fan_path, path); + tmp = tmp->next; + fclose(file); + } + } + + free(path); + path = NULL; +} + + +t_sensors *find_sensors() +{ + + t_sensors *sensors_head = NULL; + t_sensors *s = NULL; + + char *path = NULL; + const char *path_begin = "/sys/devices/platform/coretemp.0/temp"; + const char *path_end = "_input"; + + int path_size = strlen(path_begin) + strlen(path_end) + 2; + char number[1]; + sprintf(number,"%d",0); + + int i = 0; + for(i = 0; i<10; i++) { + path = (char*) malloc(sizeof( char ) * path_size); + + sprintf(number,"%d",i); + path[0] = '\0'; + strncat( path, path_begin, strlen(path_begin) ); + strncat( path, number, strlen(number) ); + strncat( path, path_end, strlen(path_begin) ); + + FILE *file = fopen(path, "r"); + + if(file != NULL) { + s = (t_sensors *) malloc( sizeof( t_sensors ) ); + s->path = (char *) malloc(sizeof( char ) * path_size); + strcpy(s->path, path); + int result = fscanf(file, "%d", &s->temperature); + if (sensors_head == NULL) { + sensors_head = s; + sensors_head->next = NULL; + } else { + t_sensors *tmp = sensors_head; + while (tmp->next != NULL) { + tmp = tmp->next; + } + tmp->next = s; + tmp->next->next = NULL; + } + fclose(file); + } + free(path); + path = NULL; + } + if(sensors_head != NULL) + find_fans(sensors_head); + return sensors_head; +} + +t_sensors *refresh_sensors(t_sensors *sensors) +{ + + t_sensors *tmp = sensors; + + while(tmp != NULL) { + FILE *file = fopen(tmp->path, "r"); + + if(file != NULL) { + int result = fscanf(file, "%d", &tmp->temperature); + fclose(file); + } + + tmp = tmp->next; + } + return sensors; +} + + + +/* Controls the speed of the fan */ +void set_fan_speed(t_sensors* sensors, unsigned short speed) +{ + + t_sensors *tmp = sensors; + + while(tmp != NULL) { + FILE *file = fopen(tmp->path, "rw"); + + if(file != NULL) { + fprintf(file, "%d", speed); + fclose(file); + } + + tmp = tmp->next; + } + +} + + + +/* Returns average CPU temp in degrees (ceiling) */ +unsigned short get_temp(t_sensors* sensors) +{ + sensors = refresh_sensors(sensors); + int sum_temp = 0; + unsigned short temp = 0; + + t_sensors* tmp = sensors; + while(tmp != NULL) { + sum_temp += tmp->temperature; + tmp = tmp->next; + } + temp = (unsigned short)( ceil( (float)( sum_temp ) / 2000. ) ); + return temp; +} + + +int main() +{ + unsigned short old_temp, new_temp, fan_speed, steps; + short temp_change; + float step_up, step_down; + + t_sensors* sensors = find_sensors(); + + new_temp = get_temp(sensors); + fan_speed = 2000; + set_fan_speed(sensors, fan_speed); + sleep(polling_interval); + + step_up = (float)( max_fan_speed - min_fan_speed ) / + (float)( ( max_temp - high_temp ) * ( max_temp - high_temp + 1 ) / 2 ); + + step_down = (float)( max_fan_speed - min_fan_speed ) / + (float)( ( max_temp - low_temp ) * ( max_temp - low_temp + 1 ) / 2 ); + + while(1) { + old_temp = new_temp; + new_temp = get_temp(sensors); + + if(new_temp >= max_temp && fan_speed != max_fan_speed) { + fan_speed = max_fan_speed; + } + + if(new_temp <= low_temp && fan_speed != min_fan_speed) { + fan_speed = min_fan_speed; + } + + temp_change = new_temp - old_temp; + + if(temp_change > 0 && new_temp > high_temp && new_temp < max_temp) { + steps = ( new_temp - high_temp ) * ( new_temp - high_temp + 1 ) / 2; + fan_speed = max( fan_speed, ceil(min_fan_speed + steps * step_up) ); + } + + if(temp_change < 0 && new_temp > low_temp && new_temp < max_temp) { + steps = ( max_temp - new_temp ) * ( max_temp - new_temp + 1 ) / 2; + fan_speed = min( fan_speed, floor(max_fan_speed - steps * step_down) ); + } + + set_fan_speed(sensors, fan_speed); + sleep(polling_interval); + printf("Old Temp %d: New Temp: %d, Fan Speed: %d\n", old_temp, new_temp, fan_speed); + + } + + return 0; +} diff --git a/src/mbpfan.h b/src/mbpfan.h new file mode 100644 index 0000000..33a4a9b --- /dev/null +++ b/src/mbpfan.h @@ -0,0 +1,41 @@ +/** Basic fan speed parameters + */ +extern unsigned short min_fan_speed; +extern unsigned short max_fan_speed; + +/** Temperature Thresholds + * low_temp - temperature below which fan speed will be at minimum + * high_temp - fan will increase speed when higher than this temperature + * max_temp - fan will run at full speed above this temperature */ +extern unsigned short low_temp; +extern unsigned short high_temp; +extern unsigned short max_temp; + + +/** Temperature polling interval + * Default value was 10 (seconds) + */ +extern unsigned short polling_interval; + +/** Represents a Temperature sensor + */ +struct s_sensors; +typedef struct s_sensors t_sensors; + +/** + * Detect the sensors in /sys/devices/platform/coretemp.0/temp + * Return a linked list of t_sensors (first temperature detected) + */ +t_sensors *find_sensors(); + +/** + * Detect the fans in /sys/devices/platform/applesmc.768/ + * Associate each fan to a sensor + */ +void find_fans(t_sensors *sensors); + +/** + * Given a linked list of t_sensors, refresh their detected + * temperature + */ +t_sensors *refresh_sensors(t_sensors *sensors); \ No newline at end of file diff --git a/src/mbpfan.o b/src/mbpfan.o new file mode 100644 index 0000000000000000000000000000000000000000..da206eea55e27fee0d041aeba2ca2e53fd4f031a GIT binary patch literal 19464 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 Z;UfDmE?k4I5bhFke5p6#m-LhH^MBqf52OG9 literal 0 HcmV?d00001