From 5e51f3c3a084e179665f1efd5ddecb92cbefb829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20B=C3=A9rub=C3=A9?= Date: Thu, 15 Aug 2024 23:02:35 -0400 Subject: [PATCH] Starting the port to CVITEK/SOPHGO platforms --- README.md | 57 +++++------ src/hal/plus/cvi_common.h | 135 ++++++++++++++++++++++++++ src/hal/plus/cvi_sys.h | 133 ++++++++++++++++++++++++++ src/hal/plus/cvi_vi.h | 193 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 491 insertions(+), 27 deletions(-) create mode 100644 src/hal/plus/cvi_common.h create mode 100644 src/hal/plus/cvi_sys.h create mode 100644 src/hal/plus/cvi_vi.h diff --git a/README.md b/README.md index 9876ff7..751c274 100644 --- a/README.md +++ b/README.md @@ -18,41 +18,44 @@ In spite of these design choices, Divinus boasts numerous features that cater to | SoC Family | Audio Stream | JPEG Snapshot | fMP4 Stream | RTSP Stream | On-Screen Display* | |-------------------------|:------------:|:-------------:|:-----------:|:-----------:|:------------------:| -| GM813x | ✗ | ✔️ | ✔️ | ✔️ | ✗ | -| Hi3516AV100[^1] | ↻ | ✔️ | ✔️ | ✔️ | ✔️ | -| Hi3516CV100[^2] | ↻ | ↻ | ↻ | ↻ | ↻ | -| Hi3516CV200[^3] | ↻ | ✔️ | ✔️ | ✔️ | ✔️ | -| Hi3516CV300[^4] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| Hi3516CV500[^5] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| Hi3516EV200[^6] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| GK7205V200[^7] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| Hi3519V100[^8] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| CV181x[^1] | ↻ | ↻ | ↻ | ↻ | ↻ | +| GM813x[^2] | ✗ | ✔️ | ✔️ | ✔️ | ✗ | +| Hi3516AV100[^3] | ↻ | ✔️ | ✔️ | ✔️ | ✔️ | +| Hi3516CV100[^4] | ↻ | ↻ | ↻ | ↻ | ↻ | +| Hi3516CV200[^5] | ↻ | ✔️ | ✔️ | ✔️ | ✔️ | +| Hi3516CV300[^6] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Hi3516CV500[^7] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Hi3516EV200[^8] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| GK7205V200[^9] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| Hi3519V100[^10] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | Hi3519AV100 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | Hi3559AV100 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | | T31 series | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| infinity6[^9] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| infinity6b0[^10] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| infinity6e[^11] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| infinity6c[^12] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | -| infinity6f[^13] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| infinity6[^11] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| infinity6b0[^12] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| infinity6e[^13] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| infinity6c[^14] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | +| infinity6f[^15] | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | _✔️ - supported, ↻ - in development, ✗ - unsupported, ⁿ/ₐ - not supported by hardware_ _* At the moment, only text, 24-bit and 32-bit RGB overlays are handled, matricial formats and covers are to follow_ -[^1]: Hi3516AV100 and Hi3516DV100 -[^2]: Hi3516CV100, Hi3518AV100, Hi3518CV100 and Hi3518EV100 -[^3]: Hi3516CV200 and Hi3518EV20\[0/1\] -[^4]: Hi3516CV300 and Hi3516EV100 -[^5]: Hi3516AV300, Hi3516CV500 and Hi3516DV300 -[^6]: Hi3516DV200, Hi3516EV200/300 and Hi3518EV300 -[^7]: GK7202V300, GK7205V200/300 and GK7605V100 -[^8]: Hi3516AV200 and Hi3519V101 -[^9]: SSC323, SSC325(D/DE) and SSC327(D/DE/Q) -[^10]: SSC333/35/37(DE) -[^11]: SSC30K\[D/Q\], SSC336\[D/Q\], SSC338\[D/G/Q\] and SSC339G -[^12]: SSC377(D/DE/QE) or SSC378\[DE/QE\] -[^13]: SSC379G +[^1]: CV181x[C/H], SG2000 and SG2002 +[^2]: GM8135(S), GM8136(S) and GM8138(S) +[^3]: Hi3516AV100 and Hi3516DV100 +[^4]: Hi3516CV100, Hi3518AV100, Hi3518CV100 and Hi3518EV100 +[^5]: Hi3516CV200 and Hi3518EV20\[0/1\] +[^6]: Hi3516CV300 and Hi3516EV100 +[^7]: Hi3516AV300, Hi3516CV500 and Hi3516DV300 +[^8]: Hi3516DV200, Hi3516EV200/300 and Hi3518EV300 +[^9]: GK7202V300, GK7205V200/300 and GK7605V100 +[^10]: Hi3516AV200 and Hi3519V101 +[^11]: SSC323, SSC325(D/DE) and SSC327(D/DE/Q) +[^12]: SSC333/35/37(DE) +[^13]: SSC30K\[D/Q\], SSC336\[D/Q\], SSC338\[D/G/Q\] and SSC339G +[^14]: SSC377(D/DE/QE) or SSC378\[DE/QE\] +[^15]: SSC379G ### Documentation diff --git a/src/hal/plus/cvi_common.h b/src/hal/plus/cvi_common.h new file mode 100644 index 0000000..c35ccea --- /dev/null +++ b/src/hal/plus/cvi_common.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../symbols.h" +#include "../types.h" + +#define CVI_VI_PIPE_NUM 4 + +typedef enum { + CVI_BAYER_BG, + CVI_BAYER_GB, + CVI_BAYER_GR, + CVI_BAYER_RG, + CVI_BAYER_END +} cvi_common_bayer; + +typedef enum { + CVI_COMPR_NONE, + CVI_COMPR_TILE, + CVI_COMPR_LINE, + CVI_COMPR_FRAME, + CVI_COMPR_END +} cvi_common_compr; + +typedef enum { + CVI_HDR_SDR8, + CVI_HDR_SDR10, + CVI_HDR_HDR10, + CVI_HDR_HLG, + CVI_HDR_SLF, + CVI_HDR_XDR, + CVI_HDR_END +} cvi_common_hdr; + +typedef enum { + CVI_PIXFMT_RGB888, + CVI_PIXFMT_BGR888, + CVI_PIXFMT_RGB888P, + CVI_PIXFMT_BGR888P, + CVI_PIXFMT_ARGB1555, + CVI_PIXFMT_ARGB4444, + CVI_PIXFMT_ARGB8888, + CVI_PIXFMT_RGB_BAYER_8BPP, + CVI_PIXFMT_RGB_BAYER_10BPP, + CVI_PIXFMT_RGB_BAYER_12BPP, + CVI_PIXFMT_RGB_BAYER_14BPP, + CVI_PIXFMT_RGB_BAYER_16BPP, + CVI_PIXFMT_YUV422P, + CVI_PIXFMT_YUV420P, + CVI_PIXFMT_YUV444P, + CVI_PIXFMT_YUV400, + CVI_PIXFMT_HSV888, + CVI_PIXFMT_HSV888P, + CVI_PIXFMT_NV12, + CVI_PIXFMT_NV21, + CVI_PIXFMT_NV16, + CVI_PIXFMT_NV61, + CVI_PIXFMT_YUV422_YUYV, + CVI_PIXFMT_YUV422_UYVY, + CVI_PIXFMT_YUV422_YVYU, + CVI_PIXFMT_YUV422_VYUY, + CVI_PIXFMT_FP32C1 = 32, + CVI_PIXFMT_FP32C3P, + CVI_PIXFMT_S32C1, + CVI_PIXFMT_S32C3P, + CVI_PIXFMT_U32C1, + CVI_PIXFMT_U32C3P, + CVI_PIXFMT_BF16C1, + CVI_PIXFMT_BF16C3P, + CVI_PIXFMT_S16C1, + CVI_PIXFMT_S16C3P, + CVI_PIXFMT_U16C1, + CVI_PIXFMT_U16C3P, + CVI_PIXFMT_S8C1, + CVI_PIXFMT_S8C3P, + CVI_PIXFMT_U8C1, + CVI_PIXFMT_U8C3P, + CVI_PIXFMT_8BITMODE = 48, + CVI_PIXFMT_END +} cvi_common_pixfmt; + +typedef enum { + CVI_PREC_8BPP, + CVI_PREC_10BPP, + CVI_PREC_12BPP, + CVI_PREC_14BPP, + CVI_PREC_16BPP, + CVI_PREC_END +} cvi_common_prec; + +typedef enum { + CVI_WDR_NONE, + CVI_WDR_BUILTIN, + CVI_WDR_QUDRA, + CVI_WDR_2TO1_LINE, + CVI_WDR_2TO1_FRAME, + CVI_WDR_2TO1_FRAME_FULLRATE, + CVI_WDR_3TO1_LINE, + CVI_WDR_3TO1_FRAME, + CVI_WDR_3TO1_FRAME_FULLRATE, + CVI_WDR_4TO1_LINE, + CVI_WDR_4TO1_FRAME, + CVI_WDR_4TO1_FRAME_FULLRATE, + CVI_WDR_END +} cvi_common_wdr; + +typedef struct { + unsigned int topWidth; + unsigned int bottomWidth; + unsigned int leftWidth; + unsigned int rightWidth; + unsigned int color; +} cvi_common_bord; + +typedef struct { + unsigned int width; + unsigned int height; +} cvi_common_dim; + +typedef struct { + int x; + int y; +} cvi_common_pnt; + +typedef struct { + int x; + int y; + unsigned int width; + unsigned int height; +} cvi_common_rect; diff --git a/src/hal/plus/cvi_sys.h b/src/hal/plus/cvi_sys.h new file mode 100644 index 0000000..49cc577 --- /dev/null +++ b/src/hal/plus/cvi_sys.h @@ -0,0 +1,133 @@ +#pragma once + +#include "cvi_common.h" + +#define CVI_SYS_API "1.0" + +typedef enum { + CVI_SYS_MOD_BASE, + CVI_SYS_MOD_VB, + CVI_SYS_MOD_SYS, + CVI_SYS_MOD_RGN, + CVI_SYS_MOD_CHNL, + CVI_SYS_MOD_VDEC, + CVI_SYS_MOD_VPSS, + CVI_SYS_MOD_VENC, + CVI_SYS_MOD_H264E, + CVI_SYS_MOD_JPEGE, + CVI_SYS_MOD_MPEG4E, + CVI_SYS_MOD_H265E, + CVI_SYS_MOD_JPEGD, + CVI_SYS_MOD_VO, + CVI_SYS_MOD_VI, + CVI_SYS_MOD_DIS, + CVI_SYS_MOD_RC, + CVI_SYS_MOD_AIO, + CVI_SYS_MOD_AI, + CVI_SYS_MOD_AO, + CVI_SYS_MOD_AENC, + CVI_SYS_MOD_ADEC, + CVI_SYS_MOD_AUD, + CVI_SYS_MOD_VPU, + CVI_SYS_MOD_ISP, + CVI_SYS_MOD_IVE, + CVI_SYS_MOD_USER, + CVI_SYS_MOD_PROC, + CVI_SYS_MOD_LOG, + CVI_SYS_MOD_H264D, + CVI_SYS_MOD_GDC, + CVI_SYS_MOD_PHOTO, + CVI_SYS_MOD_FB, + CVI_SYS_MOD_END +} cvi_sys_mod; + +typedef enum { + CVI_SYS_OPER_VIOFF_VPSSOFF, + CVI_SYS_OPER_VIOFF_VPSSON, + CVI_SYS_OPER_VION_VPSSOFF, + CVI_SYS_OPER_VION_VPSSON, + CVI_SYS_OPER_VIOFF_POSTON, + CVI_SYS_OPER_VIOFF_POSTOFF, + CVI_SYS_OPER_VION_POSTOFF, + CVI_SYS_OPER_VION_POSTON, + CVI_SYS_OPER_END +} cvi_sys_oper; + +typedef struct { + cvi_sys_mod module; + int device; + int channel; +} cvi_sys_bind; + +typedef struct { + char version[128]; +} cvi_sys_ver; + +typedef struct { + void *handle, *handleVoiceEngine, *handleVqe, *handleMisc; + + int (*fnExit)(void); + int (*fnGetChipId)(unsigned int *chip); + int (*fnGetVersion)(cvi_sys_ver *version); + int (*fnInit)(void); + + int (*fnBind)(cvi_sys_bind *source, cvi_sys_bind *dest); + int (*fnUnbind)(cvi_sys_bind *source, cvi_sys_bind *dest); + + int (*fnGetViVpssMode)(cvi_sys_oper *mode[CVI_VI_PIPE_NUM]); + int (*fnSetViVpssMode)(cvi_sys_oper *mode[CVI_VI_PIPE_NUM]); +} cvi_sys_impl; + +static int cvi_sys_load(cvi_sys_impl *sys_lib) { + if (!(sys_lib->handleMisc = dlopen("libmisc.so", RTLD_LAZY | RTLD_GLOBAL)) || + !(sys_lib->handleVqe = dlopen("libcvi_vqe.so", RTLD_LAZY | RTLD_GLOBAL)) || + !(sys_lib->handleVoiceEngine = dlopen("libcvi_VoiceEngine.so", RTLD_LAZY | RTLD_GLOBAL)) || + !(sys_lib->handle = dlopen("libsys.so", RTLD_LAZY | RTLD_GLOBAL))) + HAL_ERROR("cvi_sys", "Failed to load library!\nError: %s\n", dlerror()); + + if (!(sys_lib->fnExit = (int(*)(void)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_Exit"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnGetChipId = (int(*)(unsigned int *chip)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_GetChipId"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnGetVersion = (int(*)(cvi_sys_ver *version)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_GetVersion"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnInit = (int(*)(void)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_Init"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnBind = (int(*)(cvi_sys_bind *source, cvi_sys_bind *dest)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_Bind"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnUnbind = (int(*)(cvi_sys_bind *source, cvi_sys_bind *dest)) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_UnBind"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnGetViVpssMode = (int(*)(cvi_sys_oper *mode[CVI_VI_PIPE_NUM])) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_GetVIVPSSMode"))) + return EXIT_FAILURE; + + if (!(sys_lib->fnSetViVpssMode = (int(*)(cvi_sys_oper *mode[CVI_VI_PIPE_NUM])) + hal_symbol_load("cvi_sys", sys_lib->handle, "CVI_SYS_SetVIVPSSMode"))) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static void cvi_sys_unload(cvi_sys_impl *sys_lib) { + if (sys_lib->handleMisc) dlclose(sys_lib->handleMisc); + sys_lib->handleMisc = NULL; + if (sys_lib->handleVqe) dlclose(sys_lib->handleVqe); + sys_lib->handleVqe = NULL; + if (sys_lib->handleVoiceEngine) dlclose(sys_lib->handleVoiceEngine); + sys_lib->handleVoiceEngine = NULL; + if (sys_lib->handle) dlclose(sys_lib->handle); + sys_lib->handle = NULL; + memset(sys_lib, 0, sizeof(*sys_lib)); +} \ No newline at end of file diff --git a/src/hal/plus/cvi_vi.h b/src/hal/plus/cvi_vi.h new file mode 100644 index 0000000..65b444d --- /dev/null +++ b/src/hal/plus/cvi_vi.h @@ -0,0 +1,193 @@ +#pragma once + +#include "cvi_common.h" + +typedef enum { + CVI_VI_INTF_BT656, + CVI_VI_INTF_BT601, + CVI_VI_INTF_DIGITAL_CAMERA, + CVI_VI_INTF_BT1120_STANDARD, + CVI_VI_INTF_BT1120_INTERLEAVED, + CVI_VI_INTF_MIPI, + CVI_VI_INTF_MIPI_YUV420_NORMAL, + CVI_VI_INTF_MIPI_YUV420_LEGACY, + CVI_VI_INTF_MIPI_YUV422, + CVI_VI_INTF_LVDS, + CVI_VI_INTF_HISPI, + CVI_VI_INTF_SLVS, + CVI_VI_INTF_END +} cvi_vi_intf; + +typedef enum { + CVI_VI_SEQ_VUVU, + CVI_VI_SEQ_UVUV, + CVI_VI_SEQ_UYVY, + CVI_VI_SEQ_VYUY, + CVI_VI_SEQ_YUYV, + CVI_VI_SEQ_YVYU, + CVI_VI_SEQ_END +} cvi_vi_seq; + +typedef enum { + CVI_VI_WORK_1MULTIPLEX, + CVI_VI_WORK_2MULTIPLEX, + CVI_VI_WORK_3MULTIPLEX, + CVI_VI_WORK_4MULTIPLEX +} cvi_vi_work; + +typedef struct { + unsigned int num; + int pipeId[CVI_VI_PIPE_NUM]; +} cvi_vi_bind; + +typedef struct { + cvi_common_dim size; + cvi_common_pixfmt pixFmt; + cvi_common_hdr dynRange; + int reserved; + cvi_common_compr compress; + char mirror; + char flip; + unsigned int depth; + int srcFps; + int dstFps; + unsigned int bindVbPool; +} cvi_vi_chn; + +typedef struct { + // Accepts values from 0-2 (no, front, back) + int bypass; + char yuvSkipOn; + char ispBypassOn; + cvi_common_dim maxSize; + cvi_common_pixfmt pixFmt; + cvi_common_compr compress; + cvi_common_prec prec; + char nRedOn; + char sharpenOn; + int srcFps; + int dstFps; + char discProPicOn; + char yuvBypassOn; +} cvi_vi_pipe; + +typedef struct { + unsigned int hsyncFront; + unsigned int hsyncWidth; + unsigned int hsyncBack; + unsigned int vsyncFront; + unsigned int vsyncWidth; + unsigned int vsyncBack; + // Next three are valid on interlace mode + // and define even-frame timings + unsigned int vsyncIntrlFront; + unsigned int vsyncIntrlWidth; + unsigned int vsyncIntrlBack; +} cvi_vi_timing; + +typedef struct { + int vsyncPulse; + int vsyncInv; + int hsyncPulse; + int hsyncInv; + int vsyncValid; + int vsyncValidInv; + cvi_vi_timing timing; +} cvi_vi_sync; + +typedef struct { + cvi_vi_intf intf; + cvi_vi_work work; + int progressiveOn; + int adChn[4]; + cvi_vi_seq seq; + cvi_vi_sync sync; + // Accepts values from 0-2 (yuv, rgb, early yuv) + int rgbMode; + cvi_common_dim size; + cvi_vi_wdr wdr; + cvi_common_bayer bayerMode; + unsigned int chnNum; + unsigned int snrFps; +} cvi_vi_dev; + +typedef struct { + cvi_common_wdr mode; + unsigned int cacheLine; + char synthWdrOn; +} cvi_vi_wdr; + +typedef struct { + void *handle; + + int (*fnDisableDevice)(int device); + int (*fnEnableDevice)(int device); + int (*fnSetDeviceConfig)(int device, cvi_vi_dev *config); + + int (*fnDisableChannel)(int pipe, int channel); + int (*fnEnableChannel)(int pipe, int channel); + int (*fnSetChannelConfig)(int pipe, int channel, cvi_vi_chn *config); + + int (*fnBindPipe)(int device, cvi_vi_bind *config); + int (*fnCreatePipe)(int pipe, cvi_vi_pipe *config); + int (*fnDestroyPipe)(int pipe); + int (*fnStartPipe)(int pipe); + int (*fnStopPipe)(int pipe); +} cvi_vi_impl; + +static int cvi_vi_load(cvi_vi_impl *vi_lib) { + if ( !(vi_lib->handle = dlopen("libvpu.so", RTLD_LAZY | RTLD_GLOBAL))) + HAL_ERROR("cvi_vi", "Failed to load library!\nError: %s\n", dlerror()); + + if (!(vi_lib->fnDisableDevice = (int(*)(int device)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_DisableDev"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnEnableDevice = (int(*)(int device)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_EnableDev"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnSetDeviceConfig = (int(*)(int device, cvi_vi_dev *config)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_SetDevAttr"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnDisableChannel = (int(*)(int pipe, int channel)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_DisableChn"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnEnableChannel = (int(*)(int pipe, int channel)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_EnableChn"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnSetChannelConfig = (int(*)(int pipe, int channel, cvi_vi_chn *config)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_SetChnAttr"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnBindPipe = (int(*)(int device, cvi_vi_bind *config)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_SetDevBindPipe"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnCreatePipe = (int(*)(int pipe, cvi_vi_pipe *config)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_CreatePipe"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnDestroyPipe = (int(*)(int pipe)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_DestroyPipe"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnStartPipe = (int(*)(int pipe)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_StartPipe"))) + return EXIT_FAILURE; + + if (!(vi_lib->fnStopPipe = (int(*)(int pipe)) + hal_symbol_load("cvi_vi", vi_lib->handle, "CVI_VI_StopPipe"))) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static void cvi_vi_unload(cvi_vi_impl *vi_lib) { + if (vi_lib->handle) dlclose(vi_lib->handle); + vi_lib->handle = NULL; + memset(vi_lib, 0, sizeof(*vi_lib)); +} \ No newline at end of file