diff --git a/scripts/package.sh b/scripts/package.sh index 419e4f8..87e21d9 100755 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -157,7 +157,7 @@ printf '0\n' > "$STAGE/kvm/now_fps" printf '60\n' > "$STAGE/kvm/qlty" printf '1920\n' > "$STAGE/kvm/width" printf '1080\n' > "$STAGE/kvm/height" -printf '1\n' > "$STAGE/kvm/state" +printf '0\n' > "$STAGE/kvm/state" printf 'mjpeg\n' > "$STAGE/kvm/type" printf '0' > "$STAGE/kvm/res" diff --git a/server/common/kvm_vision.go b/server/common/kvm_vision.go index 03a85b8..2a38a9d 100644 --- a/server/common/kvm_vision.go +++ b/server/common/kvm_vision.go @@ -116,6 +116,16 @@ func (k *KvmVision) SetHDMI(enable bool) int { return result } +func (k *KvmVision) HasHDMISignal() bool { + k.mutex.RLock() + defer k.mutex.RUnlock() + if k.closed { + return false + } + + return C.kvmv_hdmi_signal_active() != 0 +} + func (k *KvmVision) SetGop(gop uint8) { k.mutex.RLock() defer k.mutex.RUnlock() diff --git a/server/dl_lib/libkvm.so b/server/dl_lib/libkvm.so index a4beeca..f1c774c 100644 Binary files a/server/dl_lib/libkvm.so and b/server/dl_lib/libkvm.so differ diff --git a/server/go.mod b/server/go.mod index 08af276..1950c7b 100644 --- a/server/go.mod +++ b/server/go.mod @@ -21,6 +21,7 @@ require ( github.com/spf13/viper v1.19.0 github.com/unrolled/secure v1.15.0 golang.org/x/crypto v0.45.0 + golang.org/x/sys v0.41.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -79,7 +80,6 @@ require ( golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.31.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/server/include/kvm_vision.h b/server/include/kvm_vision.h index 4f7af4f..9fb0367 100644 --- a/server/include/kvm_vision.h +++ b/server/include/kvm_vision.h @@ -66,6 +66,7 @@ void set_h264_gop(uint8_t _gop); void set_frame_detact(uint8_t _frame_detact); void kvmv_deinit(); uint8_t kvmv_hdmi_control(uint8_t _en); +uint8_t kvmv_hdmi_signal_active(void); #ifdef __cplusplus } diff --git a/server/proto/vm.go b/server/proto/vm.go index 75aa452..aa4c2d5 100644 --- a/server/proto/vm.go +++ b/server/proto/vm.go @@ -99,6 +99,7 @@ type GetOLEDRsp struct { type GetGetHdmiStateRsp struct { Enabled bool `json:"enabled"` + Signal bool `json:"signal"` IdleTimeout int `json:"idleTimeout"` } diff --git a/server/service/vm/hdmi.go b/server/service/vm/hdmi.go index a1dd55c..199f7fe 100644 --- a/server/service/vm/hdmi.go +++ b/server/service/vm/hdmi.go @@ -27,6 +27,7 @@ var ( setHDMI = func(enabled bool) { common.GetKvmVision().SetHDMI(enabled) } isHdmiDisabled = utils.IsHdmiDisabled getHdmiIdleTimeout = utils.GetHDMIIdleTimeout + getHdmiSignal = func() bool { return common.GetKvmVision().HasHDMISignal() } ) func (s *Service) ResetHdmi(c *gin.Context) { @@ -63,10 +64,12 @@ func (s *Service) DisableHdmi(c *gin.Context) { func (s *Service) GetHdmiState(c *gin.Context) { var rsp proto.Response + enabled := !isHdmiDisabled() rsp.OkRspWithData(c, &proto.GetGetHdmiStateRsp{ - Enabled: !utils.IsHdmiDisabled(), - IdleTimeout: utils.GetHDMIIdleTimeout(), + Enabled: enabled, + Signal: enabled && getHdmiSignal(), + IdleTimeout: getHdmiIdleTimeout(), }) log.Debug("get hdmi state") diff --git a/support/sg2002/additional/kvm/include/kvm_vision.h b/support/sg2002/additional/kvm/include/kvm_vision.h index 687789e..5537401 100644 --- a/support/sg2002/additional/kvm/include/kvm_vision.h +++ b/support/sg2002/additional/kvm/include/kvm_vision.h @@ -74,9 +74,10 @@ void set_h264_gop(uint8_t _gop); void set_frame_detact(uint8_t _frame_detact); void kvmv_deinit(); uint8_t kvmv_hdmi_control(uint8_t _en); +uint8_t kvmv_hdmi_signal_active(void); #ifdef __cplusplus } #endif -#endif // KVM_VISION_H_ \ No newline at end of file +#endif // KVM_VISION_H_ diff --git a/support/sg2002/additional/kvm/src/kvm_vision.cpp b/support/sg2002/additional/kvm/src/kvm_vision.cpp index cc67957..07d0769 100644 --- a/support/sg2002/additional/kvm/src/kvm_vision.cpp +++ b/support/sg2002/additional/kvm/src/kvm_vision.cpp @@ -13,6 +13,9 @@ #include "vi_state_shared.hpp" #include "internal/vi_state_writer.hpp" +#include +#include + #define default_venc_chn 1 #define VENC_MJPEG 0 @@ -43,6 +46,7 @@ #define vi_height_path "/kvmapp/kvm/height" #define hdmi_mode_path "/etc/kvm/hdmi_mode" #define hdmi_state_path "/proc/lt_int" +#define hdmi_signal_file_path "/kvmapp/kvm/state" #define watchdog_mode_path "/etc/kvm/watchdog" #define watchdog_temp_path "/tmp/watchdog" #define watchdog_file "/tmp/nanokvm_wd" @@ -53,6 +57,7 @@ #define LT6911_WRITE 0x00 pthread_mutex_t vi_mutex; +pthread_mutex_t hdmi_signal_mutex = PTHREAD_MUTEX_INITIALIZER; static char NanoKVM_edit[] = { 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x41,0x0C,0x33,0xC2,0x66,0xBA,0x00,0x00, @@ -77,7 +82,6 @@ typedef struct { uint16_t vpss_height = default_vpss_height; uint8_t venc_type; uint16_t qlty; - uint8_t cam_state = 0; uint8_t stream_stop; uint8_t frame_detact = 0; uint8_t display; @@ -125,6 +129,83 @@ uint8_t kvmv_data_buffer_index = 0; uint8_t debug_en = 0; uint8_t last_vi_state_code = 0; uint32_t last_vi_state_refresh_ms = 0; +uint8_t hdmi_capture_enabled = 0; +uint8_t hdmi_signal_active = 0; + +void debug(const char *format, ...); + +static bool write_hdmi_signal_file(uint8_t active) +{ + char temp_path[] = "/kvmapp/kvm/.state.tmp.XXXXXX"; + const char *state = active != 0 ? "1\n" : "0\n"; + const size_t state_size = 2; + int fd = mkstemp(temp_path); + if(fd < 0){ + debug("[hdmi] failed to create state file: %d\n", errno); + return false; + } + + int failure = 0; + if(fchmod(fd, 0644) != 0){ + failure = errno; + } + + size_t written = 0; + while(failure == 0 && written < state_size){ + ssize_t result = write(fd, state + written, state_size - written); + if(result > 0){ + written += (size_t)result; + } else if(result < 0 && errno == EINTR){ + continue; + } else { + failure = result < 0 ? errno : EIO; + } + } + + if(failure == 0 && fsync(fd) != 0){ + failure = errno; + } + if(close(fd) != 0 && failure == 0){ + failure = errno; + } + if(failure == 0 && rename(temp_path, hdmi_signal_file_path) != 0){ + failure = errno; + } + if(failure != 0){ + unlink(temp_path); + debug("[hdmi] failed to publish state file: %d\n", failure); + return false; + } + return true; +} + +static void set_hdmi_signal_state(uint8_t active) +{ + pthread_mutex_lock(&hdmi_signal_mutex); + uint8_t enabled = __atomic_load_n(&hdmi_capture_enabled, __ATOMIC_ACQUIRE); + uint8_t next = enabled != 0 && active != 0 ? 1 : 0; + uint8_t previous = __atomic_exchange_n(&hdmi_signal_active, next, __ATOMIC_ACQ_REL); + if(previous != next){ + write_hdmi_signal_file(next); + } + pthread_mutex_unlock(&hdmi_signal_mutex); +} + +static void set_hdmi_capture_enabled(uint8_t enabled) +{ + pthread_mutex_lock(&hdmi_signal_mutex); + __atomic_store_n(&hdmi_capture_enabled, enabled != 0 ? 1 : 0, __ATOMIC_RELEASE); + __atomic_store_n(&hdmi_signal_active, 0, __ATOMIC_RELEASE); + write_hdmi_signal_file(0); + pthread_mutex_unlock(&hdmi_signal_mutex); +} + +static void set_hdmi_detection_state(uint8_t active) +{ + __atomic_store_n(&kvmv_cfg.hdmi_cable_state, active != 0 ? 1 : 0, __ATOMIC_RELEASE); + set_hdmi_signal_state(active); +} + void debug(const char *format, ...) { if(debug_en){ @@ -136,8 +217,9 @@ uint8_t refresh_vi_state() { // The driver read blocks for about a second; mark the deadline before it so // that the publication cadence measures wall-clock time, not read time plus it. - last_vi_state_refresh_ms = vi_state_shared::monotonic_ms(); + last_vi_state_refresh_ms = vi_state_shared::monotonic_ms(); last_vi_state_code = vi_state_shared::refresh(); + set_hdmi_detection_state(last_vi_state_code == 1 || last_vi_state_code >= 3); return last_vi_state_code; } @@ -1176,7 +1258,7 @@ void* vi_subsystem_detection(void * arg) if(lt6911_get_hdmi_res()){ // hdmi get res debug("[hdmi] C HDMI cable insertion!\n"); - kvmv_cfg.hdmi_cable_state = 1; + set_hdmi_detection_state(1); kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height); if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1; else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){ @@ -1188,7 +1270,7 @@ void* vi_subsystem_detection(void * arg) } else { // HDMI res = 0*0/x*0 debug("[hdmi] C HDMI cable unplugged!\n"); - kvmv_cfg.hdmi_cable_state = 0; + set_hdmi_detection_state(0); } lt6911_disable(); } @@ -1201,7 +1283,7 @@ void* vi_subsystem_detection(void * arg) if(lt6911_get_hdmi_res()){ // hdmi get res debug("[hdmi] UXC HDMI cable insertion!\n"); - kvmv_cfg.hdmi_cable_state = 1; + set_hdmi_detection_state(1); kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height); if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1; else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){ @@ -1213,7 +1295,7 @@ void* vi_subsystem_detection(void * arg) } else { // HDMI res = 0*0/x*0 debug("[hdmi] UXC HDMI cable unplugged!\n"); - kvmv_cfg.hdmi_cable_state = 0; + set_hdmi_detection_state(0); } lt6911_disable(); } @@ -1226,7 +1308,7 @@ void* vi_subsystem_detection(void * arg) if(lt6911_get_hdmi_res()){ // hdmi get res debug("[hdmi] D HDMI cable insertion!\n"); - kvmv_cfg.hdmi_cable_state = 1; + set_hdmi_detection_state(1); kvmv_cfg.hdmi_res_type = lt6911_get_csi_res(&kvmv_cfg.vi_width, &kvmv_cfg.vi_height); if (kvmv_cfg.hdmi_res_type == NEW_RES) kvmv_cfg.reopen_cam_flag = 1; else if (kvmv_cfg.hdmi_res_type == UNKNOWN_RES){ @@ -1238,7 +1320,7 @@ void* vi_subsystem_detection(void * arg) } else { // HDMI res = 0*0/x*0 debug("[hdmi] D HDMI cable unplugged!\n"); - kvmv_cfg.hdmi_cable_state = 0; + set_hdmi_detection_state(0); } lt6911_disable(); } @@ -1639,7 +1721,7 @@ void kvmv_init(uint8_t _debug_info_en) uint8_t check_kvmv(uint8_t _try_num) { - if(kvmv_cfg.hdmi_cable_state == 0){ + if(__atomic_load_n(&kvmv_cfg.hdmi_cable_state, __ATOMIC_ACQUIRE) == 0){ debug("[kvmv]HDMI Cable not exist!\n"); return 0; } @@ -1788,17 +1870,8 @@ int kvmv_read_img(uint16_t _width, uint16_t _height, uint8_t _type, uint16_t _ql } } } - if(kvmv_cfg.cam_state == 0) { - kvmv_cfg.cam_state = 1; - kvmv_cfg.hdmi_cable_state = 1; - system("echo 1 > /kvmapp/kvm/state"); - } } else { - if(kvmv_cfg.cam_state == 1) { - kvmv_cfg.cam_state = 0; - system("echo 0 > /kvmapp/kvm/state"); - } - delete img; + delete img; debug("[kvmv]can`t get img...\n"); continue; // pthread_mutex_unlock(&vi_mutex); @@ -1905,6 +1978,7 @@ void free_all_kvmv_data() void kvmv_deinit() { + set_hdmi_capture_enabled(0); pthread_mutex_destroy(&vi_mutex); kvmv_cfg.try_exit_thread = 1; cam->close(); @@ -1914,6 +1988,10 @@ void kvmv_deinit() uint8_t kvmv_hdmi_control(uint8_t _en) { + if(_en == 0){ + set_hdmi_capture_enabled(0); + } + if(kvmv_cfg.hw_version == 0){ FILE *fp; @@ -1935,6 +2013,9 @@ uint8_t kvmv_hdmi_control(uint8_t _en) } if(kvmv_cfg.hw_version != 'p'){ debug("[kvmv]Hardware not support!\n"); + if(_en != 0){ + set_hdmi_capture_enabled(1); + } return -1; } if(access("/sys/class/gpio/gpio451/value", F_OK) != 0){ @@ -1954,7 +2035,15 @@ uint8_t kvmv_hdmi_control(uint8_t _en) // Reopening it here or from kvmv_read_img can exhaust the carveout // heap while the HDMI detector is transitioning. kvmv_cfg.fresh_frame_count = fresh_frame_discard_count; + set_hdmi_capture_enabled(1); return 0; } return -1; } + +uint8_t kvmv_hdmi_signal_active() +{ + uint8_t enabled = __atomic_load_n(&hdmi_capture_enabled, __ATOMIC_ACQUIRE); + uint8_t active = __atomic_load_n(&hdmi_signal_active, __ATOMIC_ACQUIRE); + return enabled != 0 && active != 0 ? 1 : 0; +} diff --git a/support/sg2002/kvm_system/main/lib/system_init/system_init.cpp b/support/sg2002/kvm_system/main/lib/system_init/system_init.cpp index 950dee7..b9f7d6e 100644 --- a/support/sg2002/kvm_system/main/lib/system_init/system_init.cpp +++ b/support/sg2002/kvm_system/main/lib/system_init/system_init.cpp @@ -1,12 +1,47 @@ #include "config.h" #include "system_init.h" +#include +#include + using namespace maix; using namespace maix::sys; extern kvm_sys_state_t kvm_sys_state; extern kvm_oled_state_t kvm_oled_state; +static void write_hdmi_state(uint8_t active) +{ + char temp_path[] = "/kvmapp/kvm/.state.init.XXXXXX"; + const char *state = active != 0 ? "1\n" : "0\n"; + int fd = mkstemp(temp_path); + if(fd < 0){ + printf("failed to create HDMI state file: %d\n", errno); + return; + } + + int failure = 0; + if(fchmod(fd, 0644) != 0){ + failure = errno; + } + if(failure == 0 && write(fd, state, 2) != 2){ + failure = errno != 0 ? errno : EIO; + } + if(failure == 0 && fsync(fd) != 0){ + failure = errno; + } + if(close(fd) != 0 && failure == 0){ + failure = errno; + } + if(failure == 0 && rename(temp_path, "/kvmapp/kvm/state") != 0){ + failure = errno; + } + if(failure != 0){ + unlink(temp_path); + printf("failed to publish HDMI state file: %d\n", failure); + } +} + uint8_t get_hdmi_version() { FILE *fp; @@ -101,7 +136,7 @@ void new_app_init(void) system("echo 2000 > /kvmapp/kvm/qlty"); system("echo 720 > /kvmapp/kvm/res"); system("echo h264 > /kvmapp/kvm/type"); - system("echo 0 > /kvmapp/kvm/state"); + write_hdmi_state(0); system("touch /etc/kvm/frame_detact"); // rm jpg_stream & kvm_stream