Fancontrol: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
Wulf (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Wulf (Diskussion | Beiträge) |
||
| Zeile 525: | Zeile 525: | ||
data_pwm = calloc(2, sizeof(struct s_temp_pwm)); | data_pwm = calloc(2, sizeof(struct s_temp_pwm)); | ||
for (int h = 0; h < 2; h++) { | for (int h = 0; h < 2; h++) { | ||
data_pwm[h] = | data_pwm[h] = calloc(count_fans, sizeof(data_pwm[0])); | ||
for (int r = 0; r < count_fans; r++) | for (int r = 0; r < count_fans; r++) | ||
data_pwm[h][r] = calloc(MAX_STEP, sizeof(data_pwm[0][0])); | data_pwm[h][r] = calloc(MAX_STEP, sizeof(data_pwm[0][0])); | ||
Version vom 12. Oktober 2019, 10:43 Uhr
This program monitors temperatures of both system and hard drives and changes fan speeds accordingly.
C program fwcontrol.c
// Author: Frank Wulf
// Version: 2.1 (2019-06-01)
//
// This program monitors temperatures of both system and hard drives and
// changes fan speeds accordingly.
//
// Copyright (C) 2017 Frank Wulf
//
// 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.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <scsi/sg.h>
enum {BUFSIZE = 256};
enum {FAN_MODE = 1};
enum {MAX_STEP = 10};
enum {
SMART_BUFFER_SIZE = 512,
SMART_SENSE_BUFFER_SIZE = 32,
SMART_CMD_LENGTH = 12
};
enum {
ATA_OP_CHECKPOWERMODE1 = 0xe5,
ATA_OP_CHECKPOWERMODE2 = 0x98,
ATA_USING_LBA = (1 << 6),
ATA_STAT_DRQ = (1 << 3),
ATA_STAT_ERR = (1 << 0),
};
enum {
SG_ATA_16 = 0x85,
SG_ATA_16_LEN = 16,
SG_ATA_PROTO_NON_DATA = (3 << 1),
SG_CDB2_CHECK_COND = (1 << 5)
};
enum {SYS, HDD};
struct s_fan {
char name[20];
char control[2];
char pwm_enable[50];
char pwm_write[50];
char interpolate[2];
short hyst[2];
unsigned short interval[2];
unsigned short decr_delay;
unsigned short stop_delay;
unsigned short count_scan[2];
unsigned short count_step[2];
unsigned char idle_pwm;
unsigned char error_pwm[2];
char loglevel;
short temp[2];
unsigned char pwm[2];
unsigned char actual_pwm;
time_t next_check[2];
time_t min_decr_time;
time_t min_stop_time;
} *fan;
struct s_temp_pwm {
short temp;
unsigned char pwm;
} ***data_pwm;
struct s_buf_sys {
char name[50];
short temp;
} *buf_sys;
struct s_buf_hdd {
char name[10];
short temp;
} *buf_hdd;
char ***scan_hdd, ***scan_sys;
char first_check = 1;
unsigned short count_fans, count_total[2], max_per_fan[2];
time_t now, next_check;
static inline int write_fan(char *name, unsigned char value) {
FILE *fp;
if ((fp = fopen(name, "w")) == NULL) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
fprintf(fp, "%d", value);
fclose(fp);
return 0;
}
int read_fan_conf(short count_only) {
FILE *fp;
size_t len;
char *name, *value, *value_w_comma, *total[2] = { NULL }, buf[BUFSIZE];
short i = -1, j, k[2], t, per_fan[2];
now = time(NULL);
if ((fp = fopen("/etc/fwcontrol.conf", "r")) == NULL) {
syslog(LOG_ERR, "Error opening configuration file\n");
return -1;
}
while (fgets(buf, BUFSIZE, fp) != NULL) {
if (buf[0] == '[') {
if (count_only) {
++count_fans;
per_fan[SYS] = per_fan[HDD] = 0;
} else {
strcpy(fan[++i].name, strtok(buf, "[]\n"));
k[SYS] = k[HDD] = 0;
}
} else if (i >= 0 || count_only) {
name = strtok(buf, "=");
value = strtok(NULL, "=");
value = strtok(value, "\n");
if (count_only) {
if (strcmp(name, "sys_input") == 0 ||
strcmp(name, "scan_hdd") == 0) {
t = strstr(name, "sys") ? SYS : HDD;
value = strtok(value, ",");
while (value != NULL) {
value_w_comma = malloc(strlen(value) + 2);
strcpy(value_w_comma, value);
strcat(value_w_comma, ",");
if (count_total[t] == 0) {
total[t] = calloc(1, strlen(value_w_comma) + 1);
strcat(total[t], value_w_comma);
++count_total[t];
} else if (strstr(total[t], value_w_comma) == NULL) {
len = strlen(total[t]) + strlen(value_w_comma) + 1;
total[t] = realloc(total[t], len);
strcat(total[t], value_w_comma);
++count_total[t];
}
free(value_w_comma);
if (++per_fan[t] > max_per_fan[t])
++max_per_fan[t];
value = strtok(NULL, ",");
}
}
} else {
t = strstr(name, "sys") ? SYS : HDD;
if (strncmp(name, "control_by_", 11) == 0) {
fan[i].control[t] = atoi(value);
fan[i].next_check[t] = now;
fan[i].error_pwm[t] = 255;
} else if (strcmp(name, "pwm_enable") == 0) {
strcpy(fan[i].pwm_enable, value);
write_fan(fan[i].pwm_enable, FAN_MODE);
} else if (strcmp(name, "pwm_write") == 0) {
strcpy(fan[i].pwm_write, value);
} else if (strcmp(name, "stop_delay") == 0) {
fan[i].stop_delay = atoi(value);
} else if (strcmp(name, "decrease_delay") == 0) {
fan[i].decr_delay = atoi(value);
} else if (strcmp(name, "loglevel") == 0) {
fan[i].loglevel = atoi(value);
} else if (strncmp(name, "interval_", 9) == 0) {
fan[i].interval[t] = atoi(value);
} else if (strncmp(name, "interpolate_", 12) == 0) {
fan[i].interpolate[t] = atoi(value);
} else if (strncmp(name, "hyst_", 5) == 0) {
fan[i].hyst[t] = atoi(value);
} else if (strcmp(name, "idle_pwm") == 0) {
fan[i].idle_pwm = atoi(value);
} else if (strncmp(name, "error_pwm_", 10) == 0) {
fan[i].error_pwm[t] = atoi(value);
} else if (strcmp(name, "sys_input") == 0 ||
strcmp(name, "scan_hdd") == 0 ) {
value = strtok(value, ",");
while (value != NULL) {
if (t == SYS)
strcpy(scan_sys[i][k[t]++], value);
else
strcpy(scan_hdd[i][k[t]++], value);
value = strtok(NULL, ",");
}
fan[i].count_scan[t] = k[t];
} else if (strncmp(name, "temp_pwm_", 9) == 0) {
j = 0;
value = strtok(value, ",");
while (value != NULL && ++j <= MAX_STEP) {
data_pwm[t][i][j-1].temp = atoi(value);
value = strtok(NULL, ",");
data_pwm[t][i][j-1].pwm = atoi(value);
value = strtok(NULL, ",");
}
fan[i].count_step[t] = j;
}
}
}
}
fclose(fp);
if (count_only) {
free(total[SYS]);
free(total[HDD]);
syslog(LOG_NOTICE,
"Controlling %d %s by monitoring %d system %s and %d %s",
count_fans, (count_fans == 1) ? "fan" : "fans", count_total[SYS],
(count_total[SYS] == 1) ? "sensor" : "sensors", count_total[HDD],
(count_total[HDD] == 1) ? "hard drive" : "hard drives");
}
return 0;
}
static inline int sgio_send(int fd, unsigned char cmd, unsigned char *rv) {
unsigned char cdb[SG_ATA_16_LEN], sb[32], *desc, status, error;
sg_io_hdr_t io_hdr;
memset(&cdb, 0, sizeof(cdb));
memset(&sb, 0, sizeof(sb));
memset(&io_hdr, 0, sizeof(io_hdr));
cdb[0] = SG_ATA_16;
cdb[1] = SG_ATA_PROTO_NON_DATA;
cdb[2] = SG_CDB2_CHECK_COND;
cdb[13] = ATA_USING_LBA;
cdb[14] = cmd;
io_hdr.cmd_len = SG_ATA_16_LEN;
io_hdr.interface_id = 'S';
io_hdr.mx_sb_len = sizeof(sb);
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.dxfer_len = 0;
io_hdr.dxferp = NULL;
io_hdr.cmdp = cdb;
io_hdr.sbp = sb;
io_hdr.pack_id = 0;
io_hdr.timeout = 500; // Milliseconds
if (ioctl(fd, SG_IO, &io_hdr) == -1) {
syslog(LOG_ERR, "ioctl() failed (cmd %u, %s)\n", cmd, strerror(errno));
return -1;
}
desc = sb + 8;
status = desc[13];
error = desc[3];
if (rv)
*rv = desc[5];
if (status & (ATA_STAT_ERR | ATA_STAT_DRQ)) {
syslog(LOG_ERR, "SG_IO command %u failed (status %u, error %u)\n",
cmd, status, error);
return -1;
}
return 0;
}
static inline char get_hdd_status(char *name) {
int fd, ret;
unsigned char state;
if ((fd = open(name, O_RDONLY)) < 0) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
ret = 1;
if (sgio_send(fd, ATA_OP_CHECKPOWERMODE1, &state) &&
sgio_send(fd, ATA_OP_CHECKPOWERMODE2, &state))
ret = 0;
close(fd);
return (state == 0) ? 0 : 1; // 0 = Sleeping, 1 = Running
}
static inline short get_hdd_temp(char *name) {
short hdd_temp;
int fd;
unsigned char buffer[SMART_BUFFER_SIZE] = "";
unsigned char sense_buffer[SMART_SENSE_BUFFER_SIZE];
unsigned char smart_read_cdb[SMART_CMD_LENGTH] =
{0xa1, 0x0c, 0x0e, 0xd0, 1, 0, 0x4f, 0xc2, 0, 0xb0, 0, 0};
sg_io_hdr_t io_hdr;
if ((fd = open(name, O_RDONLY)) < 0) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = SMART_CMD_LENGTH;
io_hdr.mx_sb_len = SMART_SENSE_BUFFER_SIZE;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = SMART_BUFFER_SIZE;
io_hdr.dxferp = buffer;
io_hdr.cmdp = smart_read_cdb;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 500; // Milliseconds
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
syslog(LOG_ERR, "ioctl() call for reading temperature failed\n");
close(fd);
return -1;
}
for (register int i = 2; i < 361; i += 12)
if ((int)buffer[i] == 194) {
hdd_temp = ((long long int)((buffer[i+5])|
(buffer[i+6]<<8)|
(buffer[i+7]<<16)|
(buffer[i+8]<<24)|
((long long int)buffer[i+9]<<32)|
((long long int)buffer[i+10]<<40))) & 0xFF;
break;
}
close(fd);
return hdd_temp;
}
static inline short get_sys_temp(char *name) {
FILE *fp;
int sys_temp;
if ((fp = fopen(name, "r")) == NULL) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
fscanf(fp, "%d", &sys_temp);
fclose(fp);
sys_temp /= 1000;
return sys_temp;
}
static inline unsigned char calc_pwm(char t, int i, short temp) {
unsigned char pwm = 0;
short j = 0;
while (temp >= data_pwm[t][i][j].temp) {
pwm = data_pwm[t][i][j].pwm;
if (++j >= fan[i].count_step[t])
break;
}
if (fan[i].interpolate[t] == 1 && j > 0 && j < fan[i].count_step[t])
pwm = ((float)data_pwm[t][i][j].pwm - data_pwm[t][i][j-1].pwm) /
(data_pwm[t][i][j].temp - data_pwm[t][i][j-1].temp) *
(temp - data_pwm[t][i][j-1].temp) + data_pwm[t][i][j-1].pwm + 0.5;
return pwm;
}
static inline int control_fan_speed(void) {
struct stat device;
unsigned char new_pwm;
char sw_checked, sw_error;
short i, j, k, type, temp, dev_temp, count_buffer[2] = { 0 };
now = time(NULL);
next_check = 0;
for (i = 0; i < count_fans; i++) {
sw_checked = 0;
for (type = 0; type < 2; type++) {
if (fan[i].control[type] == 1 && now >= fan[i].next_check[type]) {
temp = 0;
sw_error = 0;
for (j = 0; j < fan[i].count_scan[type]; j++) {
dev_temp = 0;
// Check if temperature is in buffer
for (k = 0; k < count_buffer[type]; k++)
if (type == SYS && strcmp(buf_sys[k].name,
scan_sys[i][j]) == 0) {
dev_temp = buf_sys[k].temp;
break;
} else if (type == HDD && strcmp(buf_hdd[k].name,
scan_hdd[i][j]) == 0) {
dev_temp = buf_hdd[k].temp;
break;
}
if (k == count_buffer[type]) {
if (type == HDD && stat(scan_hdd[i][j], &device) == 0 &&
(device.st_mode & S_IFMT) == S_IFBLK &&
get_hdd_status(scan_hdd[i][j]) == 1)
// Get hard drive temperature
dev_temp = get_hdd_temp(scan_hdd[i][j]);
else if (type == SYS)
// Get system temperature
dev_temp = get_sys_temp(scan_sys[i][j]);
if (dev_temp < 0)
sw_error = 1;
// Write temperature to buffer
if (type == SYS) {
strcpy(buf_sys[count_buffer[SYS]].name,
scan_sys[i][j]);
buf_sys[count_buffer[SYS]++].temp = dev_temp;
} else {
strcpy(buf_hdd[count_buffer[HDD]].name,
scan_hdd[i][j]);
buf_hdd[count_buffer[HDD]++].temp = dev_temp;
}
}
if (dev_temp > temp)
temp = dev_temp;
}
if (sw_error && calc_pwm(type, i, temp) <
fan[i].error_pwm[type])
fan[i].pwm[type] = fan[i].error_pwm[type];
else if (!(temp < fan[i].temp[type] && (fan[i].hyst[type] >
fan[i].temp[type] - temp || now < fan[i].min_decr_time)) &&
(fan[i].temp[type] != temp || first_check)) {
fan[i].pwm[type] = calc_pwm(type, i, temp);
fan[i].temp[type] = temp;
}
while ((fan[i].next_check[type] += fan[i].interval[type]) <=
now);
sw_checked = 1;
}
if (fan[i].control[type] == 1 && (next_check == 0 ||
next_check > fan[i].next_check[type]))
next_check = fan[i].next_check[type];
}
if (!sw_checked)
continue;
if (fan[i].pwm[SYS] > fan[i].pwm[HDD])
new_pwm = fan[i].pwm[SYS];
else
new_pwm = fan[i].pwm[HDD];
if (new_pwm == fan[i].actual_pwm && !first_check)
continue;
else if (new_pwm < fan[i].actual_pwm)
// Check if decrease delay time has passed
if (now < fan[i].min_decr_time)
continue;
else if (new_pwm == 0)
// Check if stop delay time has passed
if (now < fan[i].min_stop_time)
if (fan[i].idle_pwm != 0 && fan[i].idle_pwm !=
fan[i].actual_pwm)
// Set fan speed to idle
new_pwm = fan[i].idle_pwm;
else
continue;
// Set new fan speed
if (write_fan(fan[i].pwm_write, new_pwm) != 0)
continue;
if (fan[i].loglevel >= 1)
if (first_check)
syslog(LOG_NOTICE,
"%s: PWM set to %d (SYS: %d°%s, HDD: %d°%s)\n",
fan[i].name, new_pwm, fan[i].temp[SYS],
(fan[i].pwm[SYS] < new_pwm) ? "C" : "C*", fan[i].temp[HDD],
(fan[i].pwm[HDD] < new_pwm) ? "C" : "C*");
else
syslog(LOG_NOTICE,
"%s: PWM changed from %d to %d (SYS: %d°%s, HDD: %d°%s)\n",
fan[i].name, fan[i].actual_pwm, new_pwm, fan[i].temp[SYS],
(fan[i].pwm[SYS] < new_pwm) ? "C" : "C*", fan[i].temp[HDD],
(fan[i].pwm[HDD] < new_pwm) ? "C" : "C*");
if (fan[i].actual_pwm < new_pwm)
fan[i].min_decr_time = now + fan[i].decr_delay;
if (fan[i].actual_pwm == 0 && new_pwm != 0)
fan[i].min_stop_time = now + fan[i].stop_delay;
fan[i].actual_pwm = new_pwm;
}
if (first_check)
first_check = 0;
return 0;
}
int main (int argc, char **argv) {
openlog("fwcontrol", LOG_PID | LOG_CONS | LOG_NDELAY, LOG_LOCAL0);
syslog(LOG_NOTICE, "fwcontrol started ...\n");
// Determine number of configured fans, system sensors and hard drives and
// allocate memory accordingly
read_fan_conf(1);
fan = calloc(count_fans, sizeof(struct s_fan));
buf_sys = calloc(count_total[SYS], sizeof(struct s_buf_sys));
buf_hdd = calloc(count_total[HDD], sizeof(struct s_buf_hdd));
data_pwm = calloc(2, sizeof(struct s_temp_pwm));
for (int h = 0; h < 2; h++) {
data_pwm[h] = calloc(count_fans, sizeof(data_pwm[0]));
for (int r = 0; r < count_fans; r++)
data_pwm[h][r] = calloc(MAX_STEP, sizeof(data_pwm[0][0]));
}
scan_sys = calloc(count_fans, sizeof(char**));
for (int h = 0; h < count_fans; h++) {
scan_sys[h] = calloc(max_per_fan[SYS], sizeof(char*));
for (int r = 0; r < max_per_fan[SYS]; r++)
scan_sys[h][r] = calloc(50, sizeof(char));
}
scan_hdd = calloc(count_fans, sizeof(char**));
for (int h = 0; h < count_fans; h++) {
scan_hdd[h] = calloc(max_per_fan[HDD], sizeof(char*));
for (int r = 0; r < max_per_fan[HDD]; r++)
scan_hdd[h][r] = calloc(10, sizeof(char));
}
// Read fan configuration
read_fan_conf(0);
while (1) {
control_fan_speed();
sleep(next_check - now);
}
syslog(LOG_NOTICE, "fwcontrol terminated.\n");
closelog();
return EXIT_SUCCESS;
}
Configuration file /etc/fwcontrol.conf
# Configuration file for fwcontrol
#
# Parameter Value Description
# ================+=============+===============================================
# control_by_sys | 0 or 1 | If set to 1 the fan is controlled by system
# | | temperatures like CPU/GPU or other internal
# | | sensors.
# control_by_hdd | 0 or 1 | If set to 1 the fan is controlled by hard
# | | drive temperatures.
# pwm_enable | filename | Used to set the fan mode.
# pwm_write | filename | Used to change the fan speed.
# sys_input | filename(s) | Defines which system temperature sensors
# | | should be checked. Values can be written comma
# | | separated and/or in multiple lines:
# | | sys_input=/path1/filename1,/path2/filename2...
# | | sys_input=/path6/filename6,/path7/filename7...
# | | ...
# temp_pwm_sys | temp,pwm | Defines the PWM value for a given system
# | | temperature: temp1,pwm1,temp2,pwm2,...
# interpolate_sys | 0 or 1 | If set to 1 the program calculates
# | | intermediate values between the data points
# | | defined in "temp_pwm_sys".
# hyst_sys | temp | System temperature must be decreased by at
# | | least this value until fan speed can be
# | | reduced.
# scan_hdd | device(s) | Defines which hard drives should be checked.
# | | Values can be written comma separated and/or
# | | in multiple lines:
# | | scan_hdd=/dev/<hdd1>,/dev/<hdd2>...
# | | scan_hdd=/dev/<hhd6>,/dev/<hdd7>...
# | | ...
# temp_pwm_hdd | temp,pwm | Defines the PWM value for a given hard drive
# | | temperature: temp1,pwm1,temp2,pwm2,...
# interpolate_hdd | 0 or 1 | If set to 1 the program calculates
# | | intermediate values between the data points
# | | defined in "temp_pwm_hdd".
# hyst_hdd | temp | Hard drive temperature must be decreased by at
# | | least this value until fan speed can be
# | | reduced.
# decrease_delay | seconds | Defines how many seconds must have passed
# | | since last speed increase until the speed can
# | | be decreased.
# stop_delay | seconds | Defines how many seconds must have passed
# | | since start of the fan until the fan can be
# | | stopped.
# idle_pwm | 0-255 | If the fan should be stopped but stop delay
# | | time has not yet passed, then the fan speed
# | | is set to this value.
# error_pwm_sys | 0-255 | If something goes wrong when reading the
# | | system temperatures then the fan speed is set
# | | to this value (default is 255).
# error_pwm_hdd | 0-255 | If something goes wrong when reading the hard
# | | drive temperatures then the fan speed is set
# | | to this value (default is 255).
# interval_sys | seconds | Check system temperatures every n seconds.
# interval_hdd | seconds | Check hard drive temperatures every n seconds.
# loglevel | 0 or 1 | If set to 1 then every speed change will be
# | | written to the logfile.
# ================+=============+===============================================
[CPU Cooler]
control_by_sys=1
control_by_hdd=0
pwm_enable=/sys/class/hwmon/hwmon2/pwm2_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm2
sys_input=/sys/class/hwmon/hwmon1/temp1_input
temp_pwm_sys=0,20,29,20,30,40,41,40,61,200,70,255
interpolate_sys=1
hyst_sys=4
decrease_delay=30
stop_delay=120
idle_pwm=20
interval_sys=3
loglevel=1
[Front left]
control_by_sys=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm3_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm3
sys_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdb
temp_pwm_sys=0,108,44,144,48,186,60,255
temp_pwm_hdd=35,108,36,144,37,186,38,228,39,255
interpolate_sys=0
interpolate_hdd=0
hyst_sys=4
hyst_hdd=2
decrease_delay=30
stop_delay=120
idle_pwm=78
interval_sys=15
interval_hdd=60
loglevel=1
[Front right]
control_by_sys=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm4_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm4
sys_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_sys=60,255
temp_pwm_hdd=34,120,35,138,36,156,37,178,38,222,39,255
interpolate_sys=0
interpolate_hdd=0
hyst_sys=4
hyst_hdd=2
decrease_delay=30
stop_delay=120
idle_pwm=78
interval_sys=15
interval_hdd=60
loglevel=1
[Rear left]
control_by_sys=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm1_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm1
sys_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_sys=0,80,42,88,46,106,50,146,54,218,60,255
temp_pwm_hdd=36,106,37,146,38,192,39,255
interpolate_sys=0
interpolate_hdd=0
hyst_sys=4
hyst_hdd=2
decrease_delay=30
stop_delay=120
idle_pwm=80
interval_sys=15
interval_hdd=60
loglevel=1
[Rear right]
control_by_sys=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm5_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm5
sys_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_sys=0,78,42,86,46,104,50,142,54,210,60,255
temp_pwm_hdd=36,104,37,142,38,188,39,255
interpolate_sys=0
interpolate_hdd=0
hyst_sys=4
hyst_hdd=2
decrease_delay=30
stop_delay=120
idle_pwm=78
interval_sys=15
interval_hdd=60
loglevel=1
Start fwcontrol during boot
Option 1: Using init script /etc/init.d/fwcontrol
#! /bin/sh
### BEGIN INIT INFO
# Provides: fwcontrol
# Required-Start: $syslog $remote_fs
# Required-Stop: $syslog $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: fwcontrol
# Description: Control of fan speeds
### END INIT INFO
NAME="fwcontrol"
DESC="Control of fan speeds"
DAEMON=/usr/sbin/${NAME}
FWUSER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
test -f $DAEMON || exit 0
mkdir -p /var/run
PIDFILE=/var/run/${NAME}.pid
RETVAL=0
case "$1" in
start)
echo -n "Starting ${DESC}: "
start-stop-daemon --start --oknodo --quiet --background --make-pidfile --pidfile ${PIDFILE} --chuid ${FWUSER} --exec ${DAEMON}
RETVAL=$?
echo "${NAME}"
;;
stop)
echo -n "Stopping ${DESC}: "
start-stop-daemon --stop --oknodo --quiet --pidfile ${PIDFILE} --remove-pidfile --chuid ${FWUSER} --exec ${DAEMON}
RETVAL=$?
echo "${NAME}"
;;
restart|force-reload)
$0 stop
sleep 5
$0 start
RETVAL=$?
;;
*)
echo "Usage: /etc/init.d/${NAME} {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit $RETVAL
Option 2: Using systemd service /etc/systemd/system/fwcontrol.service
[Unit]
Description=fwcontrol daemon
[Service]
Type=simple
Restart=on-failure
ExecStartPre=-/bin/bash -c '/usr/bin/killall -q fwcontrol; exit 0'
ExecStart=/usr/sbin/fwcontrol
ExecStop=/usr/bin/killall fwcontrol
[Install]
WantedBy=multi-user.target
Logfile Configuration
File /etc/rsyslog.d/10-fwcontrol.conf:
local0.* /var/log/fwcontrol.log
& stop
Logfile rotation configured in file /etc/logrotate.d/fwcontrol:
/var/log/fwcontrol.log {
weekly
rotate 4
compress
delaycompress
notifempty
missingok
}