fand.c (3728B)
1 /* 2 * mcbinfand - fan daemon for the MACCHIATObin board 3 * Copyright (C) 2020 Matteo Croce <mcroce@microsoft.com> 4 * Copyright (C) 2025 Mikolaj Lenczewski <mikolaj@lenczewski.org> 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <https://www.gnu.org/licenses/>. 18 */ 19 20 #include <assert.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <signal.h> 24 #include <setjmp.h> 25 #include <time.h> 26 #include <unistd.h> 27 28 #include <gpiod.h> 29 30 #define GPIO_DEV "/dev/gpiochip1" 31 #define GPIO_DEV_OFFSET 16 32 33 #define THERM_PATH "/sys/class/thermal/thermal_zone0/temp" 34 35 #define MIN_TEMP 40000 36 #define MAX_TEMP 55000 37 38 /* 1 kHz consumes around 0.7% of a CPU */ 39 #define PWM_USECS 1000 40 41 // #define DEBUG 42 43 static jmp_buf loopjmp; 44 45 static void 46 sighandler(int _unused) 47 { 48 longjmp(loopjmp, 1); 49 } 50 51 static long 52 read_temp() 53 { 54 char buf[16] = { 0 }; 55 static FILE *ftemp; 56 long temp; 57 58 if (!ftemp) 59 ftemp = fopen(THERM_PATH, "r"); 60 61 /* Protect from burning if temp can't be read */ 62 if (!ftemp) 63 return MAX_TEMP; 64 65 fseek(ftemp, 0, SEEK_SET); 66 fread(buf, 1, sizeof(buf), ftemp); 67 68 /* Same here */ 69 if (ferror(ftemp)) { 70 perror("fread"); 71 fclose(ftemp); 72 ftemp = NULL; 73 return MAX_TEMP; 74 } 75 76 temp = strtol(buf, NULL, 10); 77 78 return temp; 79 } 80 81 static int 82 duty_cycle(int temp) 83 { 84 if (temp < MIN_TEMP) 85 return 0; 86 87 if (temp >= MAX_TEMP) 88 return PWM_USECS; 89 90 /* Scale the duty cycle from MIN_TEMP to MAX_TEMP */ 91 return (temp - MIN_TEMP) * PWM_USECS / (MAX_TEMP - MIN_TEMP); 92 } 93 94 int 95 main(int argc, char *argv[]) 96 { 97 struct gpiod_chip *chip; 98 struct gpiod_line_config *cfg; 99 struct gpiod_line_settings *settings; 100 struct gpiod_line_request *line; 101 struct timespec oldtp; 102 int on = PWM_USECS; 103 int off = 0; 104 105 chip = gpiod_chip_open(GPIO_DEV); 106 assert(chip); 107 108 cfg = gpiod_line_config_new(); 109 assert(cfg); 110 111 settings = gpiod_line_settings_new(); 112 assert(settings); 113 114 gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT); 115 gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_ACTIVE); 116 117 unsigned offset = GPIO_DEV_OFFSET; 118 gpiod_line_config_add_line_settings(cfg, &offset, 1, settings); 119 120 line = gpiod_chip_request_lines(chip, NULL, cfg); 121 assert(line); 122 123 gpiod_line_request_set_value(line, offset, GPIOD_LINE_VALUE_INACTIVE); 124 125 signal(SIGINT, sighandler); 126 signal(SIGQUIT, sighandler); 127 signal(SIGTERM, sighandler); 128 129 clock_gettime(CLOCK_MONOTONIC, &oldtp); 130 131 while (!setjmp(loopjmp)) { 132 struct timespec now; 133 134 clock_gettime(CLOCK_MONOTONIC, &now); 135 if (now.tv_sec > oldtp.tv_sec) { 136 int temp = read_temp(); 137 138 oldtp = now; 139 on = duty_cycle(temp); 140 off = PWM_USECS - on; 141 142 #ifdef DEBUG 143 printf("temp: %0.1f, duty cycle: %d%%: (%d on, %d off)\n", 144 temp / 1000.0, on * 100 / PWM_USECS, on, off); 145 #endif 146 } 147 148 if (on) { 149 gpiod_line_request_set_value(line, offset, GPIOD_LINE_VALUE_ACTIVE); 150 usleep(on); 151 } 152 153 if (off) { 154 gpiod_line_request_set_value(line, offset, GPIOD_LINE_VALUE_INACTIVE); 155 usleep(off); 156 } 157 } 158 159 puts("exiting..."); 160 161 /* Don't leave the fan off and burn the CPU */ 162 usleep(100000); 163 gpiod_line_request_set_value(line, offset, GPIOD_LINE_VALUE_ACTIVE); 164 gpiod_line_request_release(line); 165 gpiod_chip_close(chip); 166 167 return 0; 168 }