238 lines
8.0 KiB
C
238 lines
8.0 KiB
C
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/signalfd.h>
|
|
#include <unistd.h>
|
|
|
|
#include "aio.h"
|
|
#include "cpu.h"
|
|
#include "gpu.h"
|
|
#include "lcd.h"
|
|
#include "log.h"
|
|
#include "sys.h"
|
|
#include "utils.h"
|
|
#include "version.h"
|
|
|
|
|
|
static constexpr uint8_t lcd_contrast = 200;
|
|
static constexpr uint16_t refresh_interval_ms = 1000;
|
|
static constexpr char netif[] = "enp4s0f0";
|
|
static constexpr char cpu_pcidev[] = "0000:00:18.3";
|
|
static constexpr char gpu_pcidev[] = "0000:0a:00.0";
|
|
|
|
static struct {
|
|
pthread_mutex_t sensors_mtx;
|
|
pthread_cond_t sensors_ready;
|
|
atomic_bool running;
|
|
} control;
|
|
|
|
struct sensors_t { // NOLINT stfu
|
|
time_t ts;
|
|
struct memory_t ram;
|
|
struct disk_t home;
|
|
struct disk_t gry;
|
|
struct network_t net;
|
|
struct syssensors_t sys;
|
|
struct cpu_sensors_t cpu;
|
|
struct gpu_sensors_t gpu;
|
|
struct aio_t aio;
|
|
double loadavg[3];
|
|
};
|
|
|
|
static void *sensors_thread(void *arg) {
|
|
applog(LOG_START, "sensors thread started, refresh interval is %dms", refresh_interval_ms);
|
|
struct sensors_t *sensors = arg;
|
|
|
|
struct timespec next;
|
|
clock_gettime(CLOCK_REALTIME, &next);
|
|
|
|
/* set up paths for the sensors */
|
|
strcpy(sensors->home.mountpoint, "/home/drath");
|
|
strcpy(sensors->gry.mountpoint, "/var/mnt/gry");
|
|
strcpy(sensors->net.device, netif);
|
|
cpu_detect(&sensors->cpu, cpu_pcidev);
|
|
gpu_detect(&sensors->gpu, gpu_pcidev);
|
|
sys_detect(&sensors->sys, "nct6775.656");
|
|
sys_netdev_detect(&sensors->net);
|
|
aio_detect(&sensors->aio);
|
|
|
|
/* main sensor loop */
|
|
applog(LOG_START, "reached sensors main loop");
|
|
while (control.running) { // NOLINT
|
|
pthread_mutex_lock(&control.sensors_mtx);
|
|
|
|
{
|
|
sensors->ts = time(nullptr);
|
|
sys_get_load_avg(sensors->loadavg);
|
|
sys_get_ram(&sensors->ram);
|
|
sys_get_disk(&sensors->home);
|
|
sys_get_disk(&sensors->gry);
|
|
sys_get_network(&sensors->net);
|
|
sys_update_sensors(&sensors->sys);
|
|
cpu_update(&sensors->cpu);
|
|
gpu_update(&sensors->gpu);
|
|
aio_update(&sensors->aio);
|
|
}
|
|
|
|
pthread_cond_signal(&control.sensors_ready);
|
|
pthread_mutex_unlock(&control.sensors_mtx);
|
|
|
|
timespec_add_ms(&next, refresh_interval_ms);
|
|
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
|
|
}
|
|
|
|
aio_close(&sensors->aio);
|
|
applog(LOG_WRN, "sensors thread stopped");
|
|
return nullptr;
|
|
}
|
|
|
|
static void *display_thread(void *arg) {
|
|
applog(LOG_START, "display thread started");
|
|
const struct sensors_t *sensors = arg;
|
|
|
|
/* lcd setup */
|
|
struct lcd_t lcd = {
|
|
.port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00",
|
|
.baud = 0010002, // from termios-baud.h, B115200
|
|
};
|
|
lcd_init(&lcd);
|
|
lcd_set_backlight(&lcd, true);
|
|
lcd_set_contrast(&lcd, lcd_contrast);
|
|
|
|
lcd_write_buf(&lcd, 0, 14, "linuxSmartie");
|
|
lcd_writef(&lcd, 0, 2, "git hash:");
|
|
lcd_writef(&lcd, 0, 3, "%s", GIT_HASH);
|
|
lcd_flush_buf(&lcd);
|
|
lcd_clear_buf(&lcd);
|
|
struct timespec next;
|
|
clock_gettime(CLOCK_REALTIME, &next);
|
|
timespec_add_ms(&next, refresh_interval_ms);
|
|
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, nullptr);
|
|
|
|
/* setup custom chars */
|
|
lcd_set_custom_char(&lcd, 0, &(lcd_glyph_t){16, 6, 9, 8, 8, 9, 6, 0}); // celcius
|
|
lcd_set_custom_char(&lcd, 1, &(lcd_glyph_t){0, 0, 4, 10, 10, 17, 31, 0}); // delta
|
|
lcd_set_custom_char(&lcd, 2, &(lcd_glyph_t){0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
|
|
lcd_set_custom_char(&lcd, 3, &(lcd_glyph_t){4, 14, 21, 4, 4, 4, 0}); // up arrow
|
|
lcd_set_custom_char(&lcd, 4, &(lcd_glyph_t){4, 4, 4, 21, 14, 4, 0}); // down arrow
|
|
lcd_set_custom_char(&lcd, 5, &(lcd_glyph_t){0, 0, 15, 21, 21, 17, 15, 0}); // games (steam?)
|
|
|
|
/* main display loop */
|
|
applog(LOG_START, "reached display main loop");
|
|
while (control.running) { // NOLINT its updated globally bud
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
timespec_add_ms(&ts, (uint16_t)(refresh_interval_ms * 1.25));
|
|
pthread_mutex_lock(&control.sensors_mtx); // lock sensor struct
|
|
pthread_cond_timedwait(&control.sensors_ready, &control.sensors_mtx, &ts); // wait until data ready
|
|
|
|
{
|
|
// prepare some tricky sensors
|
|
constexpr char modes[] = "QBE";
|
|
const uint16_t avg_fan_speed = (uint16_t)((sensors->aio.fan_speed[0] + sensors->aio.fan_speed[1]) / 2);
|
|
|
|
char gpudelta[5];
|
|
const uint8_t delta = sensors->gpu.temp_hotspot - sensors->gpu.temp_edge;
|
|
snprintf(gpudelta, sizeof(gpudelta), "%c%d", 1, delta);
|
|
const uint8_t gpu_pos = delta > 10 ? 3 : 2;
|
|
|
|
char gpuclock[16];
|
|
snprintf(gpuclock, sizeof(gpuclock), "%d/%dMHz", sensors->gpu.clock_core, sensors->gpu.clock_mem);
|
|
|
|
lcd_writef(&lcd, 0, 0, "%c%s", 2, P_BYTES(sensors->home.usage.free, 1));
|
|
lcd_writef(&lcd, 7, 0, "%c%s", 5, P_BYTES(sensors->gry.usage.free, 1));
|
|
lcd_writef(&lcd, 15, 0, "%s%c", SYS_GET_TIME_STR(sensors->ts), lcd_bar(&lcd, 6, sensors->gpu.usage_core / 100.0));
|
|
lcd_writef(&lcd, 26, 0, "%s %s %s", P_NUM(sensors->loadavg[0]), P_NUM(sensors->loadavg[1]), P_NUM(sensors->loadavg[2]));
|
|
|
|
lcd_writef(&lcd, 0, 1, "%d/%d%c", sensors->cpu.tctl, sensors->cpu.tccd1, 0);
|
|
lcd_writef(&lcd, 7, 1, "%s", P_BYTES(sensors->ram.used, 0));
|
|
lcd_writef(&lcd, 13, 1, "%.1f%c", (double)sensors->aio.water_temp, 0);
|
|
lcd_writef(&lcd, 18, 1, "%3d%c", sensors->sys.t_sensor, 0);
|
|
lcd_writef(&lcd, 28, 1, "%4s%c", gpudelta, 0);
|
|
lcd_writef(&lcd, 25 - gpu_pos, 1, "%6s", P_BYTES(sensors->gpu.vram_used, 1));
|
|
lcd_writef(&lcd, 34, 1, "%d/%d%c", sensors->gpu.temp_edge, sensors->gpu.temp_hotspot, 0);
|
|
|
|
lcd_writef(&lcd, 0, 2, "%.0fW", (double)sensors->cpu.ptotal);
|
|
lcd_writef(&lcd, 5, 2, "%dR", avg_fan_speed);
|
|
lcd_writef(&lcd, 12, 2, "%5s%c", P_BYTES(sensors->net.upload, 1), 3);
|
|
lcd_writef(&lcd, 19, 2, "%c%s", 4, P_BYTES(sensors->net.download, 1));
|
|
lcd_writef(&lcd, 26, 2, "%4dR", sensors->gpu.fan_speed);
|
|
lcd_writef(&lcd, 32, 2, "%3d/%3dW", sensors->gpu.power_avg, sensors->gpu.power_cap);
|
|
|
|
lcd_writef(&lcd, 0, 3, "%uMHz", sensors->cpu.avg_freq);
|
|
lcd_writef(&lcd, 8, 3, "%4dR-%c %d", sensors->aio.pump_speed, modes[sensors->aio.pump_mode], sensors->aio.pump_counter);
|
|
lcd_writef(&lcd, 21, 3, "%12s", gpuclock);
|
|
lcd_writef(&lcd, 34, 3, "%.3fV", (double)sensors->gpu.vcore);
|
|
}
|
|
|
|
pthread_mutex_unlock(&control.sensors_mtx); // unlock sensor struct
|
|
lcd_flush_buf(&lcd);
|
|
lcd_clear_buf(&lcd);
|
|
}
|
|
|
|
lcd_write_buf(&lcd, 2, 16, "Goodbye"); // center
|
|
lcd_set_backlight(&lcd, false);
|
|
lcd_flush_buf(&lcd);
|
|
lcd_close(&lcd);
|
|
applog(LOG_WRN, "display thread stopped");
|
|
return nullptr;
|
|
}
|
|
|
|
int main() {
|
|
applog(LOG_START, "linuxSmartie %s starting", GIT_HASH);
|
|
applog(LOG_DBG, "Compiled with %s", C_STD);
|
|
|
|
/* see if another instance is up, quit if it is */
|
|
if (get_instance_lock() < 0) {
|
|
applog(LOG_ERR, "Another instance is already running. Exiting.");
|
|
return 0;
|
|
}
|
|
|
|
/* malloc memory for the struct */
|
|
struct sensors_t *sensors = malloc(sizeof(struct sensors_t)); // no actual point in mallocing but its cool
|
|
if (sensors == nullptr) {
|
|
applog(LOG_ERR, "can't allocate mem for the sensor struct. You don't got %lu bytes bro?", sizeof(*sensors));
|
|
free(sensors);
|
|
return 1;
|
|
}
|
|
applog(LOG_INF, "sensor struct allocated, size is %lu bytes", sizeof(*sensors));
|
|
|
|
/* signal setup */
|
|
sigset_t sigset;
|
|
sigemptyset(&sigset);
|
|
sigaddset(&sigset, SIGINT);
|
|
sigaddset(&sigset, SIGTERM);
|
|
sigaddset(&sigset, SIGQUIT);
|
|
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
|
|
struct signalfd_siginfo info;
|
|
const int sfd = signalfd(-1, &sigset, SFD_CLOEXEC);
|
|
|
|
/* control vars setup */
|
|
pthread_mutex_init(&control.sensors_mtx, nullptr);
|
|
pthread_cond_init(&control.sensors_ready, nullptr);
|
|
control.running = true;
|
|
|
|
/* thread setup */
|
|
pthread_t sensors_pthread;
|
|
pthread_t display_pthread;
|
|
|
|
/* actually start the threads */
|
|
pthread_create(&sensors_pthread, nullptr, sensors_thread, sensors);
|
|
pthread_create(&display_pthread, nullptr, display_thread, sensors);
|
|
|
|
/* halts here until signal to stop */
|
|
const ssize_t ret = read(sfd, &info, sizeof(info));
|
|
(void)ret;
|
|
|
|
/* cleanup on exit */
|
|
applog(LOG_WRN, "got stop signal, shutting down...");
|
|
close(sfd);
|
|
control.running = false;
|
|
pthread_join(sensors_pthread, nullptr);
|
|
pthread_join(display_pthread, nullptr);
|
|
free(sensors);
|
|
return 0;
|
|
} |