Tasmota/lib/libesp32_lvgl/lvgl/src/osal/lv_linux.c
2025-10-18 12:32:57 +02:00

221 lines
6.5 KiB
C

/**
* @file lv_linux.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_os_private.h"
#if defined(__linux__)
#include "../core/lv_global.h"
#include "../misc/lv_log.h"
#include "lv_linux.h"
#include <stdio.h>
#include <string.h>
/*********************
* DEFINES
*********************/
#define LV_UPTIME_MONITOR_FILE "/proc/stat"
#define LV_UPTIME_MONITOR_SELF_FILE "/proc/self/stat"
#define LV_PROC_STAT_VAR_FORMAT " %" PRIu32
#define LV_PROC_STAT_IGNORE_VAR_FORMAT " %*" PRIu32
#define last_proc_stat LV_GLOBAL_DEFAULT()->linux_last_proc_stat
#if LV_SYSMON_PROC_IDLE_AVAILABLE
#define last_self_ticks LV_GLOBAL_DEFAULT()->linux_last_self_proc_time_ticks
#define last_system_total_ticks_stat LV_GLOBAL_DEFAULT()->linux_last_system_total_ticks_stat
#endif
#define LV_SELF_PROC_STAT_BUFFER_SIZE 512
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static lv_result_t lv_read_proc_stat(lv_linux_proc_stat_t * result);
static uint32_t lv_proc_stat_get_total(const lv_linux_proc_stat_t * p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
uint32_t lv_os_get_idle_percent(void)
{
lv_linux_proc_stat_t proc_stat;
{
lv_result_t err = lv_read_proc_stat(&proc_stat);
if(err == LV_RESULT_INVALID) {
return UINT32_MAX;
}
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
uint32_t delta =
proc_stat.buffer[i] - last_proc_stat.buffer[i];
/* Update old for next call*/
last_proc_stat.buffer[i] = proc_stat.buffer[i];
/* Store delta in new */
proc_stat.buffer[i] = delta;
}
}
/* From here onwards, there's no risk of overflowing as long as we call this function regularly */
const uint32_t total = lv_proc_stat_get_total(&proc_stat);
if(total == 0) {
return 0;
}
return (proc_stat.fields.idle * 100) / total;
}
#if LV_SYSMON_PROC_IDLE_AVAILABLE
uint32_t lv_os_get_proc_idle_percent(void)
{
uint64_t self_current_time_ticks = 0;
lv_linux_proc_stat_t stat_current_system_total_ticks;
lv_linux_proc_stat_t stat_delta_system_ticks;
FILE * self = fopen(LV_UPTIME_MONITOR_SELF_FILE, "r");
if(!self) {
LV_LOG_ERROR("Failed to open " LV_UPTIME_MONITOR_SELF_FILE);
return UINT32_MAX;
}
char self_stat_buffer[LV_SELF_PROC_STAT_BUFFER_SIZE];
if(!fgets(self_stat_buffer, sizeof(self_stat_buffer), self)) {
fclose(self);
LV_LOG_ERROR("Failed to read " LV_UPTIME_MONITOR_SELF_FILE);
return UINT32_MAX;
}
fclose(self);
/* The comm field can contain spaces and parentheses, so we find the last ')'
* Skip the whitespace after finding the last ')' */
char * p = strrchr(self_stat_buffer, ')');
if(!p) {
LV_LOG_ERROR(LV_UPTIME_MONITOR_SELF_FILE " is missing the closing ')'");
return UINT32_MAX;
}
p++; /* move past the ')' */
while(*p && (*p == ' ' || *p == '\t'))
p++; /* skip whitespace after ')' */
if(!*p) {
LV_LOG_ERROR(LV_UPTIME_MONITOR_SELF_FILE " unexpectedly ends after the closing ')'");
return UINT32_MAX;
}
uint64_t utime = 0;
uint64_t stime = 0;
int scanned_items = sscanf(p,
"%*c " // state (field 3)
"%*d %*d %*d %*d %*d " // ppid, pgrp, session, tty_nr, tpgid (fields 4-8)
"%*u " // flags (field 9)
"%*u %*u %*u %*u " // minflt, cminflt, majflt, cmajflt (fields 10-13)
"%" SCNu64 " %" SCNu64, // utime, stime (fields 14-15)
&utime, &stime);
if(scanned_items != 2) {
LV_LOG_ERROR("Failed to parse utime/stime");
return UINT32_MAX;
}
self_current_time_ticks = utime + stime;
if(lv_read_proc_stat(&stat_current_system_total_ticks) != LV_RESULT_OK) {
LV_LOG_ERROR("lv_read_proc_stat failed");
return UINT32_MAX;
}
/* no delta on the first call so return 0, next call will have actual values*/
if(last_self_ticks == 0) {
last_self_ticks = self_current_time_ticks;
last_system_total_ticks_stat = stat_current_system_total_ticks;
return 100; /* 100% idle = 0% CPU usage*/
}
uint64_t delta_self_proc_ticks = self_current_time_ticks - last_self_ticks;
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
stat_delta_system_ticks.buffer[i] = stat_current_system_total_ticks.buffer[i] - last_system_total_ticks_stat.buffer[i];
}
uint64_t delta_total_system_ticks = lv_proc_stat_get_total(&stat_delta_system_ticks);
last_self_ticks = self_current_time_ticks;
last_system_total_ticks_stat = stat_current_system_total_ticks;
if(delta_total_system_ticks == 0) return 100;
uint32_t cpu_usage_percent = (uint32_t)((delta_self_proc_ticks * 100ULL) / delta_total_system_ticks);
return 100 - cpu_usage_percent;
}
#endif /*LV_SYSMON_PROC_IDLE_AVAILABLE*/
/**********************
* STATIC FUNCTIONS
**********************/
static lv_result_t lv_read_proc_stat(lv_linux_proc_stat_t * result)
{
FILE * fp = fopen(LV_UPTIME_MONITOR_FILE, "r");
if(!fp) {
LV_LOG_ERROR("Failed to open " LV_UPTIME_MONITOR_FILE);
return LV_RESULT_INVALID;
}
const char * fmt = "cpu " LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
LV_PROC_STAT_IGNORE_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT
LV_PROC_STAT_VAR_FORMAT LV_PROC_STAT_VAR_FORMAT;
int err = fscanf(fp, fmt, &result->fields.user, &result->fields.nice,
&result->fields.system, &result->fields.idle,
&result->fields.irq, &result->fields.softirq,
&result->fields.steal);
fclose(fp);
if(err != LV_PROC_STAT_PARAMS_LEN) {
LV_LOG_ERROR("Failed to parse " LV_UPTIME_MONITOR_FILE);
return LV_RESULT_INVALID;
}
return LV_RESULT_OK;
}
static uint32_t lv_proc_stat_get_total(const lv_linux_proc_stat_t * p)
{
uint32_t sum = 0;
for(size_t i = 0; i < LV_PROC_STAT_PARAMS_LEN; ++i) {
sum += p->buffer[i];
}
return sum;
}
#endif