diff --git a/server/dl_lib/libkvm.so b/server/dl_lib/libkvm.so index bde4c2c..79e2587 100644 Binary files a/server/dl_lib/libkvm.so and b/server/dl_lib/libkvm.so differ diff --git a/support/sg2002/additional/kvm/include/internal/vi_state_shared.hpp b/support/sg2002/additional/kvm/include/internal/vi_state_shared.hpp new file mode 100644 index 0000000..8e1c98c --- /dev/null +++ b/support/sg2002/additional/kvm/include/internal/vi_state_shared.hpp @@ -0,0 +1,107 @@ +#ifndef VI_STATE_SHARED_HPP_ +#define VI_STATE_SHARED_HPP_ + +#include +#include +#include +#include +#include +#include + +namespace vi_state_shared { + +struct State { + uint32_t dev_fps; + uint32_t fps; + uint32_t width_gt; + uint32_t width_ls; + uint32_t height_gt; + uint32_t height_ls; +}; + +uint8_t refresh(); + +namespace detail { + +static const char path[] = "/dev/shm/nanokvm_vi_state"; +static const uint32_t magic = 0x4e4b5649U; +static const uint32_t version = 1U; + +struct SharedState { + uint32_t sequence; + uint32_t magic; + uint32_t version; + uint32_t updated_ms; + State state; +}; + +inline uint32_t monotonic_ms() +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return (uint32_t)((uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000); +} + +inline const SharedState *map_reader() +{ + static const SharedState *shared = NULL; + if (shared != NULL) { + return shared; + } + + int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + return NULL; + } + struct stat stat_buf; + if (fstat(fd, &stat_buf) != 0 || stat_buf.st_size < (off_t)sizeof(*shared)) { + close(fd); + return NULL; + } + void *addr = mmap(NULL, sizeof(*shared), PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (addr == MAP_FAILED) { + return NULL; + } + shared = (const SharedState *)addr; + return shared; +} + +} // namespace detail + +inline bool read(State *state, uint32_t max_age_ms) +{ + const detail::SharedState *shared = detail::map_reader(); + if (shared == NULL) { + return false; + } + + for (int attempt = 0; attempt < 3; attempt++) { + uint32_t sequence = __atomic_load_n(&shared->sequence, __ATOMIC_ACQUIRE); + if (sequence & 1U) { + continue; + } + uint32_t magic = __atomic_load_n(&shared->magic, __ATOMIC_RELAXED); + uint32_t version = __atomic_load_n(&shared->version, __ATOMIC_RELAXED); + uint32_t updated_ms = __atomic_load_n(&shared->updated_ms, __ATOMIC_RELAXED); + State value; + value.dev_fps = __atomic_load_n(&shared->state.dev_fps, __ATOMIC_RELAXED); + value.fps = __atomic_load_n(&shared->state.fps, __ATOMIC_RELAXED); + value.width_gt = __atomic_load_n(&shared->state.width_gt, __ATOMIC_RELAXED); + value.width_ls = __atomic_load_n(&shared->state.width_ls, __ATOMIC_RELAXED); + value.height_gt = __atomic_load_n(&shared->state.height_gt, __ATOMIC_RELAXED); + value.height_ls = __atomic_load_n(&shared->state.height_ls, __ATOMIC_RELAXED); + uint32_t final_sequence = __atomic_load_n(&shared->sequence, __ATOMIC_ACQUIRE); + if (sequence == final_sequence && !(final_sequence & 1U) && + magic == detail::magic && version == detail::version && + detail::monotonic_ms() - updated_ms <= max_age_ms) { + *state = value; + return true; + } + } + return false; +} + +} // namespace vi_state_shared + +#endif // VI_STATE_SHARED_HPP_ diff --git a/support/sg2002/additional/kvm/src/kvm_vision.cpp b/support/sg2002/additional/kvm/src/kvm_vision.cpp index 86af38a..2127c20 100644 --- a/support/sg2002/additional/kvm/src/kvm_vision.cpp +++ b/support/sg2002/additional/kvm/src/kvm_vision.cpp @@ -10,6 +10,7 @@ * // free 错内存时会炸的问题 */ #include "kvm_vision.h" +#include "internal/vi_state_shared.hpp" #define default_venc_chn 1 @@ -205,71 +206,6 @@ void write_res_to_file(uint16_t _width, uint16_t _height) system("sync"); } -/* return 0 : VI not init; - * return 1 : HDMI and CSI status are normal; - * return 2 : HDMI abnormal; - * return 3 : CSI abnormal: width too small; - * return 4 : CSI abnormal: width too large; - * return 5 : CSI abnormal: height too small; - * return 6 : CSI abnormal: height too large; - * return 7 : CSI abnormal: Unknown reason; - */ -uint8_t get_vi_state() -{ - FILE *fp = fopen("/proc/cvitek/vi_dbg", "r"); - if (fp == NULL) { - return 0; - } - - unsigned int dev_fps = 0; - unsigned int fps = 0; - unsigned int width_gt = 0; - unsigned int width_ls = 0; - unsigned int height_gt = 0; - unsigned int height_ls = 0; - bool have_dev_fps = false; - bool have_fps = false; - char line[256]; - char field[32]; - unsigned int value; - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%31s : %u", field, &value) != 2) { - continue; - } - if (strcmp(field, "VIDevFPS") == 0) { - dev_fps = value; - have_dev_fps = true; - } else if (strcmp(field, "VIFPS") == 0) { - fps = value; - have_fps = true; - } else if (strcmp(field, "VICsiCh0WidthGTCnt") == 0) { - width_gt = value; - } else if (strcmp(field, "VICsiCh0WidthLSCnt") == 0) { - width_ls = value; - } else if (strcmp(field, "VICsiCh0HeightGTCnt") == 0) { - height_gt = value; - } else if (strcmp(field, "VICsiCh0HeightLSCnt") == 0) { - height_ls = value; - } - } - fclose(fp); - - if (!have_dev_fps || !have_fps) { - return 0; - } - if (dev_fps == 0) { - return 2; - } - if (fps != 0) { - return 1; - } - if (width_gt != 0) return 3; - if (width_ls != 0) return 4; - if (height_gt != 0) return 5; - if (height_ls != 0) return 6; - return 7; -} - int set_hdmi_mode(uint8_t _hdmi_mode) { if(_hdmi_mode >= 0 && _hdmi_mode <= 2){ @@ -418,7 +354,7 @@ uint8_t auto_try_res() uint8_t auto_trying_times = 0; for (auto_trying_times = 0; auto_trying_times < sizeof(hdmi_res_list)/4; auto_trying_times++){ - err_code = get_vi_state(); + err_code = vi_state_shared::refresh(); switch(err_code){ case 0: // shouldn't be possible to run here @@ -457,8 +393,8 @@ uint8_t auto_try_res() break; } } - if (get_vi_state() == 1) return 1; - if (get_vi_state() == 2) return 2; + if (vi_state_shared::refresh() == 1) return 1; + if (vi_state_shared::refresh() == 2) return 2; else return 0; } @@ -1158,6 +1094,7 @@ void* vi_subsystem_detection(void * arg) // while(!app::need_exit()) uint8_t while_count_detect_res = 0; + uint8_t while_count_publish_vi_state = 0; while(true) { if(kvmv_cfg.try_exit_thread == 1) @@ -1169,6 +1106,10 @@ void* vi_subsystem_detection(void * arg) switch (kvmv_cfg.hdmi_mode){ case 0: + while_count_publish_vi_state = (while_count_publish_vi_state + 1)%100; + if (while_count_publish_vi_state == 1) { + vi_state_shared::refresh(); + } // Switching to Mode 0 requires restarting HDMI (effective only for PCIe version) // Handling of automatic detection situations if(get_new_hdmi_mode == 1){ @@ -1324,7 +1265,7 @@ void* vi_subsystem_detection(void * arg) } else if (kvmv_cfg.vi_detect_state == 2){ // Low-frequency detection of HDMI status, no log output printf("[kvmv] kvmv_cfg.vi_detect_state == 2\n"); - err_code = get_vi_state(); + err_code = vi_state_shared::refresh(); if (err_code != 1) { kvmv_cfg.vi_detect_state = 1; } @@ -1345,7 +1286,7 @@ void* vi_subsystem_detection(void * arg) } // dbg info - err_code = get_vi_state(); + err_code = vi_state_shared::refresh(); switch(err_code){ case 0: debug("[kvmv] VI not init\n"); @@ -1375,7 +1316,7 @@ void* vi_subsystem_detection(void * arg) } } else if (kvmv_cfg.vi_detect_state == 2){ // detection of HDMI status, no log output - err_code = get_vi_state(); + err_code = vi_state_shared::refresh(); if (err_code != 1) kvmv_cfg.vi_detect_state = 1; } else { kvmv_cfg.vi_detect_state = 1; diff --git a/support/sg2002/additional/kvm/src/vi_state_shared.cpp b/support/sg2002/additional/kvm/src/vi_state_shared.cpp new file mode 100644 index 0000000..3f8002c --- /dev/null +++ b/support/sg2002/additional/kvm/src/vi_state_shared.cpp @@ -0,0 +1,114 @@ +#include "internal/vi_state_shared.hpp" + +#include +#include + +namespace vi_state_shared { +namespace { + +detail::SharedState *map_writer() +{ + static detail::SharedState *shared = NULL; + if (shared != NULL) { + return shared; + } + + int fd = open(detail::path, O_CREAT | O_RDWR | O_CLOEXEC, 0644); + if (fd < 0) { + return NULL; + } + if (ftruncate(fd, sizeof(*shared)) != 0) { + close(fd); + return NULL; + } + void *addr = mmap(NULL, sizeof(*shared), PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + close(fd); + if (addr == MAP_FAILED) { + return NULL; + } + shared = (detail::SharedState *)addr; + return shared; +} + +void publish(const State &state) +{ + detail::SharedState *shared = map_writer(); + if (shared == NULL) { + return; + } + + uint32_t sequence = __atomic_load_n(&shared->sequence, __ATOMIC_RELAXED) | 1U; + __atomic_store_n(&shared->sequence, sequence, __ATOMIC_SEQ_CST); + __atomic_store_n(&shared->magic, detail::magic, __ATOMIC_RELAXED); + __atomic_store_n(&shared->version, detail::version, __ATOMIC_RELAXED); + __atomic_store_n(&shared->updated_ms, detail::monotonic_ms(), __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.dev_fps, state.dev_fps, __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.fps, state.fps, __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.width_gt, state.width_gt, __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.width_ls, state.width_ls, __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.height_gt, state.height_gt, __ATOMIC_RELAXED); + __atomic_store_n(&shared->state.height_ls, state.height_ls, __ATOMIC_RELAXED); + __atomic_store_n(&shared->sequence, sequence + 1U, __ATOMIC_RELEASE); +} + +} // namespace + +/* return 0 : VI not init; + * return 1 : HDMI and CSI status are normal; + * return 2 : HDMI abnormal; + * return 3 : CSI abnormal: width too small; + * return 4 : CSI abnormal: width too large; + * return 5 : CSI abnormal: height too small; + * return 6 : CSI abnormal: height too large; + * return 7 : CSI abnormal: Unknown reason; + */ +uint8_t refresh() +{ + FILE *fp = fopen("/proc/cvitek/vi_dbg", "r"); + if (fp == NULL) { + return 0; + } + + State state = {}; + bool have_dev_fps = false; + bool have_fps = false; + char line[256]; + char field[32]; + unsigned int value; + while (fgets(line, sizeof(line), fp) != NULL) { + if (sscanf(line, "%31s : %u", field, &value) != 2) { + continue; + } + if (strcmp(field, "VIDevFPS") == 0) { + state.dev_fps = value; + have_dev_fps = true; + } else if (strcmp(field, "VIFPS") == 0) { + state.fps = value; + have_fps = true; + } else if (strcmp(field, "VICsiCh0WidthGTCnt") == 0) { + state.width_gt = value; + } else if (strcmp(field, "VICsiCh0WidthLSCnt") == 0) { + state.width_ls = value; + } else if (strcmp(field, "VICsiCh0HeightGTCnt") == 0) { + state.height_gt = value; + } else if (strcmp(field, "VICsiCh0HeightLSCnt") == 0) { + state.height_ls = value; + } + } + fclose(fp); + + if (!have_dev_fps || !have_fps) { + return 0; + } + publish(state); + if (state.dev_fps == 0) return 2; + if (state.fps != 0) return 1; + if (state.width_gt != 0) return 3; + if (state.width_ls != 0) return 4; + if (state.height_gt != 0) return 5; + if (state.height_ls != 0) return 6; + return 7; +} + +} // namespace vi_state_shared diff --git a/support/sg2002/kvm_system/main/CMakeLists.txt b/support/sg2002/kvm_system/main/CMakeLists.txt index 939403d..149b200 100644 --- a/support/sg2002/kvm_system/main/CMakeLists.txt +++ b/support/sg2002/kvm_system/main/CMakeLists.txt @@ -10,7 +10,7 @@ list(APPEND ADD_INCLUDE "lib/hdmi" ) -list(APPEND ADD_PRIVATE_INCLUDE "") +list(APPEND ADD_PRIVATE_INCLUDE "../../additional/kvm/include") append_srcs_dir(ADD_SRCS "src" diff --git a/support/sg2002/kvm_system/main/lib/system_state/system_state.cpp b/support/sg2002/kvm_system/main/lib/system_state/system_state.cpp index 6cab58a..fbf6f01 100644 --- a/support/sg2002/kvm_system/main/lib/system_state/system_state.cpp +++ b/support/sg2002/kvm_system/main/lib/system_state/system_state.cpp @@ -1,5 +1,6 @@ #include "config.h" #include "system_state.h" +#include "internal/vi_state_shared.hpp" #include #include #include @@ -239,22 +240,10 @@ void kvm_update_hdmi_state() static uint8_t check_times = 4; if(++check_times > 5){ check_times = 0; - FILE *fp = fopen("/proc/cvitek/vi_dbg", "r"); - if (fp == NULL) { - return; + vi_state_shared::State state; + if (vi_state_shared::read(&state, 3000U)) { + kvm_sys_state.hdmi_state = state.fps == 0 ? 0 : 1; } - - char line[256]; - char field[32]; - unsigned int value; - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%31s : %u", field, &value) == 2 && - strcmp(field, "VIFPS") == 0) { - kvm_sys_state.hdmi_state = value == 0 ? 0 : 1; - break; - } - } - fclose(fp); } }