Browse Source

Linux_SDK_V1.0.3

thead_admin 1 year ago
parent
commit
ee655750f9

+ 0 - 27
.gitignore

@@ -1,29 +1,2 @@
-/.auto.deps
-/.config.cmd
-/.config.old
-/..config.tmp
-/.config
 /.vscode
-*.tmp
-*.depend
-*.o
-*.a
-*.o.d
-*.o.cmd
-*.a.cmd
-*.ko.cmd
-*.ko
-*.mod
-*.mod.c
-*.mod.cmd
-*.order
-*.orig
-*.symvers.cmd
-*.order.cmd
-test/ivs_test
-user_mode/libivs.so
-Module.symvers
-out/
 output/
-obj/
-dependencies/

+ 33 - 106
Makefile

@@ -1,120 +1,47 @@
-##
- # Copyright (C) 2020 Alibaba Group Holding Limited
-##
 ifneq ($(wildcard ../.param),)
-  include ../.param
+	include ../.param
 endif
 
-#CONFIG_DEBUG_MODE=1
-CONFIG_OUT_ENV=hwlinux
+INC_PATH ?= /usr/include
+LIB_PATH ?= /usr/lib
 
-CONFIG_BUILD_DRV_EXTRA_PARAM:=""    	
-CONFIG_BUILD_LIB_EXTRA_PARAM:=""
-CONFIG_BUILD_TST_EXTRA_PARAM:=""	
+OUTPUTDIR = ./output
+LIBNAME = $(OUTPUTDIR)/libplink.so
+server_NAME = $(OUTPUTDIR)/plinkserver
+client_NAME = $(OUTPUTDIR)/plinkclient
+stitcher_NAME = $(OUTPUTDIR)/plinkstitcher
 
-DIR_TARGET_BASE=bsp/ivs		
-DIR_TARGET_LIB =bsp/ivs/lib
-DIR_TARGET_KO  =bsp/ivs/ko
-DIR_TARGET_TEST=bsp/ivs/test
+INCS = ./inc
+LIBSRCS = ./src/process_linker.c
+LIBOBJS = $(LIBSRCS:.c=.o)
+server_SRCS = ./test/plink_server.c
+server_OBJS = $(server_SRCS:.c=.o)
+client_SRCS = ./test/plink_client.c
+client_OBJS = $(client_SRCS:.c=.o)
+stitcher_SRCS = ./test/plink_stitcher.c
+stitcher_OBJS = $(stitcher_SRCS:.c=.o)
 
-MODULE_NAME=IVS
-BUILD_LOG_START="\033[47;30m>>> $(MODULE_NAME) $@ begin\033[0m"
-BUILD_LOG_END  ="\033[47;30m<<< $(MODULE_NAME) $@ end\033[0m"
+CFLAGS = -I$(INCS) -I$(INC_PATH)/vidmem
+CFLAGS += -pthread -fPIC -O
 
-#
-# Do a parallel build with multiple jobs, based on the number of CPUs online
-# in this system: 'make -j8' on a 8-CPU system, etc.
-#
-# (To override it, run 'make JOBS=1' and similar.)
-#
+$(shell if [ ! -e $(OUTPUTDIR) ];then mkdir -p $(OUTPUTDIR); fi)
 
-ifeq ($(JOBS),)
-  JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null)
-  ifeq ($(JOBS),)
-    JOBS := 1
-  endif
-endif
-
-all:    info driver lib test install_local_output install_rootfs
-.PHONY: info driver lib test install_local_output install_rootfs \
-        install_prepare install_addons clean_driver clean_lib clean_test clean_output clean
-
-info:
-	@echo $(BUILD_LOG_START)
-	@echo "  ====== Build Info from repo project ======"
-	@echo "    BUILDROOT_DIR="$(BUILDROOT_DIR)
-	@echo "    CROSS_COMPILE="$(CROSS_COMPILE)
-	@echo "    LINUX_DIR="$(LINUX_DIR)
-	@echo "    ARCH="$(ARCH)
-	@echo "    BOARD_NAME="$(BOARD_NAME)
-	@echo "    KERNEL_ID="$(KERNELVERSION)
-	@echo "    KERNEL_DIR="$(LINUX_DIR)
-	@echo "    INSTALL_DIR_ROOTFS="$(INSTALL_DIR_ROOTFS)
-	@echo "    INSTALL_DIR_SDK="$(INSTALL_DIR_SDK)
-	@echo "  ====== Build configuration by settings ======"
-#	@echo "    CONFIG_DEBUG_MODE="$(CONFIG_DEBUG_MODE)
-	@echo "    CONFIG_OUT_ENV="$(CONFIG_OUT_ENV)
-	@echo "    JOBS="$(JOBS)
-	@echo $(BUILD_LOG_END)
-
-driver:
-	@echo $(BUILD_LOG_START)
-	make -C $(LINUX_DIR) M=$(PWD)/kernel_mode ARCH=$(ARCH) modules
-	@echo $(BUILD_LOG_END)
-
-clean_driver:
-	@echo $(BUILD_LOG_START)
-	make -C kernel_mode KDIR=$(LINUX_DIR) clean
-	@echo $(BUILD_LOG_END)
-
-lib:
-	@echo $(BUILD_LOG_START)
-	make -w -C user_mode hwlinux
-	@echo $(BUILD_LOG_END)
-
-clean_lib:
-	@echo $(BUILD_LOG_START)
-	make clean -C user_mode
-	@echo $(BUILD_LOG_END)
-
-test: lib driver
-	@echo $(BUILD_LOG_START)
-	make -w -C test hwlinux
-	@echo $(BUILD_LOG_END)
-
-clean_test:
-	@echo $(BUILD_LOG_START)
-	make clean -C test
-	@echo $(BUILD_LOG_END)
-
-install_prepare:
-	mkdir -p ./output/rootfs/$(DIR_TARGET_KO)
-	mkdir -p ./output/rootfs/$(DIR_TARGET_LIB)
-	mkdir -p ./output/rootfs/$(DIR_TARGET_TEST)
+all: lib server client stitcher
 
-install_addons: install_prepare
-	@echo $(BUILD_LOG_START)
-	@echo $(BUILD_LOG_END)
+lib: 
+	$(CC) $(LIBSRCS) $(CFLAGS) -shared -o $(LIBNAME)
 
-install_local_output: driver lib test install_addons
-	@echo $(BUILD_LOG_START)
-	find ./kernel_mode -name "*.ko" | xargs -i cp -f {} ./output/rootfs/$(DIR_TARGET_KO)
-	find ./user_mode -name "*.so" | xargs -i cp -f {} ./output/rootfs/$(DIR_TARGET_LIB)
-	find ./user_mode -name "*.a" | xargs -i cp -f {} ./output/rootfs/$(DIR_TARGET_LIB)
-	cp -f ./test/ivs_test ./output/rootfs/$(DIR_TARGET_TEST)
-	@if [ `command -v tree` != "" ]; then \
-	    tree ./output/rootfs;             \
-	fi
-	@echo $(BUILD_LOG_END)
+server: lib
+	$(CC) $(server_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(server_NAME)
 
-install_rootfs: install_local_output
-	@echo $(BUILD_LOG_START)
-	@echo $(BUILD_LOG_END)
+client: lib
+	$(CC) $(client_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(client_NAME)
 
-clean_output:
-	@echo $(BUILD_LOG_START)
-	rm -rf ./output
-	@echo $(BUILD_LOG_END)
+stitcher: lib
+	$(CC) $(stitcher_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(stitcher_NAME)
 
-clean: clean_output clean_driver clean_lib clean_test
+clean:
+	rm -rf $(OUTPUTDIR)
 
+%.o : %.c
+	$(CC) $(CFLAGS) -c $< -o $@

+ 57 - 12
README.md

@@ -1,12 +1,57 @@
-# How to build
-- Just run 'make' to build kernel mode driver, user mode driver and test application.
-
-Find all the binaries in following folders:
-- output/rootfs/bsp/ivs/ko: kernel mode driver
-- output/rootfs/bsp/ivs/lib: user mode driver static linked library and dynamic linked library
-- output/rootfs/bsp/ivs/test: test application
-
-# Description of each directories
-- kernel_mode: kernel mode driver source
-- user_mode: user mode driver source
-- test: test application source
+# Process Linker Repository
+This git module contains a linker library which can be used to connect two process for data sharing and passing file descriptor.
+
+- **src**: c source code of process linker library.
+- **inc**: public header files of process linker library. User should include these files to use process linker.
+- **test**: sample applications. Two sample applications, server and client, are implimented for test and reference purpose.
+
+## How to build
+Just run `make` and binaries will be generated in **output** folder.
+
+## How to use
+- **libplink.so**: shared library of process link. See API doc for more details of usage.
+- **plinkserver**: sample server application
+```shell
+usage: ./plinkserver [options]
+
+  Available options:
+    -l      plink file name (default: /tmp/plink.test)
+    -i      input YUV file name (mandatory)
+    -f      input color format (default: 3)
+                2 - I420
+                3 - NV12
+                4 - P010
+                14 - Bayer Raw 10bit
+                15 - Bayer Raw 12bit
+    -w      video width (mandatory)
+    -h      video height (mandatory)
+    -s      video buffer stride in bytes (default: video width)
+    -n      number of frames to send (default: 10)
+```
+- **plinkclient**: sample client application
+```shell
+./plinkclient [frames] [plink server name] [dump file name]
+```
+
+- **plinkstitcher**: sample implementation of stitching filter, which can stitch up to 4 source videos (NV12 or RAW) into one as NV12 output
+
+```
+usage: ./plinkstitcher [options]
+
+  Stitch multiple pictures to one. Maximum # of pictures to be stitched is 4
+  Available options:
+    -i<n>   plink file name of input port #n (default: /tmp/plink.stitch.in<n>). n is 0 based.
+    -o      plink file name of output port (default: /tmp/plink.stitch.out)
+    -l      layout (default: 0)
+                0 - vertical
+                1 - horizontal
+                2 - matrix
+    -f      output color format (default: 3)
+                3 - NV12
+    -w      output video width (default: 800)
+    -h      output video height (default: 1280)
+    -s      output video buffer stride (default: video width)
+    --help  print this message
+```
+
+Please note the sample applications have dependency on **video-memory** module for memory allocating and dma-buf operations. 

BIN
doc/pics/comm.PNG


BIN
doc/pics/packet.PNG


BIN
doc/pics/plink-sw-stack.PNG


BIN
doc/pics/thead-logo-eng-white-wide.png


File diff suppressed because it is too large
+ 23 - 0
doc/plink-api.md


+ 225 - 0
inc/process_linker.h

@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef _PROCESS_LINKER_H_
+#define _PROCESS_LINKER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PLINK_VERSION_MAJOR     0
+#define PLINK_VERSION_MINOR     1
+#define PLINK_VERSION_REVISION  1
+
+/* Maximum data descriptors in one packet */
+#define PLINK_MAX_DATA_DESCS 10
+
+/* Close all the connections from client. */
+/* Can be used as the second parameter of PLINK_close when the instance is created as SERVER */
+#define PLINK_CLOSE_ALL -1
+
+/* invalid file descriptor */
+#define PLINK_INVALID_FD -1
+
+#define DATA_HEADER_SIZE (sizeof(PlinkDescHdr))
+#define DATA_SIZE(type) (sizeof(type) - DATA_HEADER_SIZE)
+
+typedef void *PlinkHandle;
+typedef void PlinkDescriptor;
+typedef int PlinkChannelID;
+
+typedef enum _PlinkStatus
+{
+    PLINK_STATUS_OK = 0,
+    PLINK_STATUS_MORE_DATA = 1,     /* have more data to parse in the receive buffer */
+    PLINK_STATUS_TIMEOUT = 2,       /* wait timeout, which means no data received within the time */
+    PLINK_STATUS_NO_DATA = 3,       /* no data recieved */
+    PLINK_STATUS_ERROR = -1,        /* general error */
+    PLINK_STATUS_WRONG_PARAMS = -2, /* wrong parameters */
+    PLINK_STATUS_NO_MEMORY = -3,    /* not enough memory */
+} PlinkStatus;
+
+/* plink mode */
+typedef enum _PlinkMode
+{
+    PLINK_MODE_SERVER = 0,      /* run plink as server; server should be launched before client */
+    PLINK_MODE_CLIENT,          /* run plink as client which can connect to server */
+    PLINK_MODE_MAX
+} PlinkMode;
+
+typedef union _PlinkVersion
+{
+    struct process_linker
+    {
+        unsigned char major;
+        unsigned char minor;
+        unsigned char revision;
+        unsigned char step;
+    } v;
+    unsigned int version;
+} PlinkVersion;
+
+typedef struct _PlinkDescHdr
+{
+    unsigned int size;      /* data size, excluding this header */
+    int type;               /* type of this data descriptor */
+    int id;                 /* buffer id if it's buffer descriptor. Only values greater than 0 are valid */
+} PlinkDescHdr;
+
+/* data packet can be sent/received in one send/recv call */
+typedef struct _PlinkPacket
+{
+    int fd;                                         /* file descriptor. If PLINK_INVALID_FD, it's invalid */
+    unsigned int timestamp;                         /* timestamp of this packet, the time for rendering */
+    int num;                                        /* number of valid data descriptor entries in list[] */
+    PlinkDescriptor *list[PLINK_MAX_DATA_DESCS];    /* list of pointers which point to data descriptor. */
+} PlinkPacket;
+
+/**
+ * \brief Create a plink instance.
+ *
+ * Create a plink object with the specified name as server or client.
+ * When mode is PLINK_MODE_SERVER, a file of the specified name will be created.
+ *
+ * \param plink Point to the pointer of plink instance.
+ * \param name Socket file name.
+ * \param mode plink mode, server or client.
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_getVersion(PlinkVersion *version);
+
+/**
+ * \brief Create a plink instance.
+ *
+ * Create a plink object with the specified name as server or client.
+ * When mode is PLINK_MODE_SERVER, a file of the specified name will be created.
+ *
+ * \param plink Point to the pointer of plink instance.
+ * \param name Socket file name.
+ * \param mode plink mode, server or client.
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_create(PlinkHandle *plink, const char *name, PlinkMode mode);
+
+/**
+ * \brief Create a connection between server and client
+ *
+ * Server calls this function to wait for connection and accept.
+ * Client calls this function to connect to server.
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel id of the new connection. Valid for server only. Should be 0 for client
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_connect(PlinkHandle plink, PlinkChannelID *channel);
+
+/**
+ * \brief Create a connection between server and client with timeout
+ *
+ * Server calls this function to wait for connection and accept with timeout.
+ * Client calls this function to connect to server with timeout.
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel id of the new connection. Valid for server only. Should be 0 for client
+ * \param timeout_ms timeout in unit of milliseconds.
+ * \return PLINK_STATUS_OK successful, 
+ * \return PLINK_STATUS_TIMEOUT if no data received within timeout_ms, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_connect_ex(PlinkHandle plink, PlinkChannelID *channel, int timeout_ms);
+
+/**
+ * \brief Send a packet
+ *
+ * Send a packet through the channel.
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel The channel to send this packet. Valid for server only. Should be 0 for client
+ * \param pkt Point to the packet to be sent.
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_send(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt);
+
+/**
+ * \brief Wait for data from channel
+ *
+ * This function returns once there is data received from the channel.
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel The channel to receive data. Valid for server only. Should be 0 for client
+ * \param timeout_ms timeout in unit of milliseconds.
+ * \return PLINK_STATUS_OK successful, 
+ * \return PLINK_STATUS_TIMEOUT if no data received within timeout_ms, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_wait(PlinkHandle plink, PlinkChannelID channel, int timeout_ms);
+
+/**
+ * \brief Receive data
+ *
+ * Receive data from the channel.
+ * Data descriptors of the packet are stored in the internal buffer, 
+ * and may be overwritten in the next PLINK_recv call. 
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel The channel to receive data. Valid for server only. Should be 0 for client
+ * \param pkt Point to the received packet.
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_recv(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt);
+
+/**
+ * \brief Receive data with timeout
+ *
+ * Receive data from the channel with timeout.
+ * Data descriptors of the packet are stored in the internal buffer, 
+ * and may be overwritten in the next PLINK_recv call. 
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel The channel to receive data. Valid for server only. Should be 0 for client
+ * \param pkt Point to the received packet.
+ * \param timeout_ms timeout in unit of milliseconds.
+ * \return PLINK_STATUS_OK successful, 
+ * \return PLINK_STATUS_TIMEOUT if no data received within timeout_ms, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_recv_ex(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt, int timeout_ms);
+
+/**
+ * \brief Close connections
+ *
+ * Close connections. Server can set channel to PLINK_CLOSE_ALL to close all connections.
+ *
+ * \param plink Pointer of plink instance.
+ * \param channel The connection to be closed. Valid for server only. Should be 0 for client
+ * \return PLINK_STATUS_OK successful, 
+ * \return other unsuccessful.
+ */
+PlinkStatus PLINK_close(PlinkHandle plink, PlinkChannelID channel);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_PROCESS_LINKER_H_ */

+ 196 - 0
inc/process_linker_types.h

@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef _PROCESS_LINKER_TYPES_H_
+#define _PROCESS_LINKER_TYPES_H_
+
+#include "process_linker.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* When set PlinkMsg.msg to this exit code, it means to close the connection */
+#define PLINK_EXIT_CODE -1
+
+/* image/video color format */
+typedef enum _PlinkColorFormat
+{
+    PLINK_COLOR_FormatUnused,
+    PLINK_COLOR_FormatMonochrome,
+    PLINK_COLOR_FormatYUV420Planar,
+    PLINK_COLOR_FormatYUV420SemiPlanar,
+    PLINK_COLOR_FormatYUV420SemiPlanarP010,
+    PLINK_COLOR_FormatYUV422Planar,
+    PLINK_COLOR_FormatYUV422SemiPlanar,
+    PLINK_COLOR_Format32bitBGRA8888,
+    PLINK_COLOR_Format32bitARGB8888,
+    PLINK_COLOR_Format24BitRGB888,
+    PLINK_COLOR_Format24BitRGB888Planar,
+    PLINK_COLOR_Format24BitBGR888,
+    PLINK_COLOR_Format24BitBGR888Planar,
+    PLINK_COLOR_FormatRawBayer8bit,
+    PLINK_COLOR_FormatRawBayer10bit,
+    PLINK_COLOR_FormatRawBayer12bit,
+    PLINK_COLOR_FormatMax
+} PlinkColorFormat;
+
+typedef enum _PlinkBayerPattern
+{
+    PLINK_BAYER_PATTERN_RGGB,
+    PLINK_BAYER_PATTERN_BGGR,
+    PLINK_BAYER_PATTERN_GRBG,
+    PLINK_BAYER_PATTERN_GBRG,
+    PLINK_BAYER_PATTERN_MAX
+} PlinkBayerPattern;
+
+/* Data descriptor type */
+typedef enum _PlinkDescType
+{
+    PLINK_TYPE_1D_BUFFER = 0,   /* PlinkBufferInfo */
+    PLINK_TYPE_2D_YUV,          /* PlinkYuvInfo */
+    PLINK_TYPE_2D_RGB,          /* PlinkRGBInfo */
+    PLINK_TYPE_OBJECT,          /* PlinkObjectInfo */
+    PLINK_TYPE_MESSAGE,         /* PlinkMsg */
+    PLINK_TYPE_TIME,            /* PlinkTimeInfo */
+    PLINK_TYPE_2D_RAW,          /* PlinkRawInfo */
+    PLINK_TYPE_MAX
+} PlinkDescType;
+
+/* time type */
+typedef enum _PlinkTimeType
+{
+    PLINK_TIME_START = 0,       /* start time */
+    PLINK_TIME_CALIBRATION,     /* time delta for calibration */
+    PLINK_TIME_MAX
+} PlinkTimeType;
+
+/* 1D buffer */
+typedef struct _PlinkBufferInfo
+{
+    PlinkDescHdr header;
+    unsigned long long bus_address;
+    unsigned int offset;
+    unsigned int size;
+} PlinkBufferInfo;
+
+/* 2D YUV surface */
+typedef struct _PlinkYuvInfo
+{
+    PlinkDescHdr header;
+    PlinkColorFormat format;
+    unsigned long long bus_address_y;
+    unsigned long long bus_address_u;
+    unsigned long long bus_address_v;
+    unsigned int offset_y;
+    unsigned int offset_u;
+    unsigned int offset_v;
+    unsigned int pic_width;
+    unsigned int pic_height;
+    unsigned int stride_y;
+    unsigned int stride_u;
+    unsigned int stride_v;
+} PlinkYuvInfo;
+
+/* 2D RGB surface */
+typedef struct _PlinkRGBInfo
+{
+    PlinkDescHdr header;
+    PlinkColorFormat format;
+    unsigned long long bus_address_r;
+    unsigned long long bus_address_g;
+    unsigned long long bus_address_b;
+    unsigned long long bus_address_a;
+    unsigned int offset_r;
+    unsigned int offset_g;
+    unsigned int offset_b;
+    unsigned int offset_a;
+    unsigned int img_width;
+    unsigned int img_height;
+    unsigned int stride_r;
+    unsigned int stride_g;
+    unsigned int stride_b;
+    unsigned int stride_a;
+} PlinkRGBInfo;
+
+/* 2D Bayer Raw surface */
+typedef struct _PlinkRawInfo
+{
+    PlinkDescHdr header;
+    PlinkColorFormat format;
+    PlinkBayerPattern pattern;
+    unsigned long long bus_address;
+    unsigned int offset;
+    unsigned int img_width;
+    unsigned int img_height;
+    unsigned int stride;
+} PlinkRawInfo;
+
+/* Feature map buffer after NPU inference */
+typedef struct _PlinkBox
+{
+    float x1;
+    float y1;
+    float x2;
+    float y2;
+} PlinkBox;
+
+typedef struct _PlinkLandmark
+{
+    float x[5];
+    float y[5];
+} PlinkLandmark;
+
+typedef struct _PlinkObjectDetect
+{
+    float score;
+    PlinkBox box;
+    PlinkLandmark landmark;
+} PlinkObjectDetect;
+
+typedef struct _PlinkObjectInfo
+{
+    PlinkDescHdr header;
+    unsigned long long bus_address;
+    unsigned int object_cnt;
+} PlinkObjectInfo;
+
+/* Used to send message */
+typedef struct _PlinkMsg
+{
+    PlinkDescHdr header;
+    int msg;                /* When greater than 0, it means the id of buffer which can be released */
+                            /* When set to 0, it means a buffer can be released, but id is unknown */
+                            /* When set to PLINK_EXIT_CODE, it means to close connection */
+                            /* Other values are reserved */
+} PlinkMsg;
+
+/* time information */
+typedef struct _PlinkTimeInfo
+{
+    PlinkDescHdr header;
+    PlinkTimeType type;
+    long long seconds;
+    long long useconds;
+} PlinkTimeInfo;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_PROCESS_LINKER_TYPES_H_ */

+ 0 - 6
kernel_mode/Makefile

@@ -1,6 +0,0 @@
-shake-objs := isp_venc_shake_driver.o
-obj-m         += shake.o
-
-clean:
-	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.ur-safe .cache.mk
-	rm -rf modules.order Module.symvers

+ 0 - 439
kernel_mode/isp_venc_shake_driver.c

@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2021 - 2022  Alibaba Group. All rights reserved.
- * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/fs.h>
-#include <linux/errno.h>
-#include <linux/ioctl.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/interrupt.h>
-#include <linux/sched.h>
-#include <linux/semaphore.h>
-#include <linux/spinlock.h>
-#include <asm/io.h>
-#include <linux/cdev.h>
-#include <linux/pci.h>
-#include <linux/uaccess.h>
-#include <linux/ioport.h>
-#include <asm/irq.h>
-#include <linux/version.h>
-#include <linux/vmalloc.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include "isp_venc_shake_driver.h"
-
-#define IVS_SWREG_AMOUNT 27
-
-typedef struct _theadivs_dev
-{
-	struct cdev cdev;
-	dev_t devt;
-	struct class *class;
-    unsigned long base_addr;
-    u32 iosize;
-    volatile u8 *hwregs;
-    int irq;
-    int state;
-} theadivs_dev;
-
-static int theadivs_major = 0;  /* dynamic */
-static int theadivs_minor = 0;
-static theadivs_dev* theadivs_data = NULL;
-static unsigned int device_register_index = 0;
-struct class *theadivs_class;
-
-static irqreturn_t theadivs_isr(int irq, void *dev_id);
-
-static void print_registers(void)
-{
-    printk("pic_width = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x00)));
-    printk("pic_height = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x04)));
-    printk("encode_width = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x08)));
-    printk("encode_height = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x0c)));
-    printk("wid_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x10)));
-    printk("wid_uv = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x14)));
-    printk("sram_size = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x18)));
-    printk("encode_n = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x1c)));
-    printk("stride_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x20)));
-    printk("stride_uv = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x24)));
-    printk("encode_x = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x2c)));
-    printk("encode_y = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x30)));
-    printk("int_state = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x38)));
-    printk("int_mask = %d\n", ioread32((void*)(theadivs_data->hwregs + 0x40)));
-}
-
-static int config_ivs(struct file *filp, struct ivs_parameter *params)
-{
-    iowrite32(params->pic_width, (void*)(theadivs_data->hwregs + 0x00));
-    iowrite32(params->pic_height, (void*)(theadivs_data->hwregs + 0x04));
-    iowrite32(params->encode_width, (void*)(theadivs_data->hwregs + 0x08));
-    iowrite32(params->encode_height, (void*)(theadivs_data->hwregs + 0x0c));
-    iowrite32(params->wid_y, (void*)(theadivs_data->hwregs + 0x10));
-    iowrite32(params->wid_uv, (void*)(theadivs_data->hwregs + 0x14));
-    iowrite32(params->sram_size, (void*)(theadivs_data->hwregs + 0x18));
-    iowrite32(params->encode_n, (void*)(theadivs_data->hwregs + 0x1c));
-    iowrite32(params->stride_y, (void*)(theadivs_data->hwregs + 0x20));
-    iowrite32(params->stride_uv, (void*)(theadivs_data->hwregs + 0x24));
-    iowrite32(1, (void*)(theadivs_data->hwregs + 0x28)); // CLEAR
-    iowrite32(params->encode_x, (void*)(theadivs_data->hwregs + 0x2c));
-    iowrite32(params->encode_y, (void*)(theadivs_data->hwregs + 0x30));
-    iowrite32(0, (void*)(theadivs_data->hwregs + 0x34)); // START
-    iowrite32(0, (void*)(theadivs_data->hwregs + 0x3c)); // INT_CLEAN
-    iowrite32(params->int_mask, (void*)(theadivs_data->hwregs + 0x40));
-
-    return 0;
-}
-
-static long theadivs_ioctl(struct file *filp,
-                          unsigned int cmd, unsigned long arg)
-{
-    int err = 0;
-
-    if (_IOC_TYPE(cmd) != THEAD_IOC_MAGIC)
-        return -ENOTTY;
-    else if (_IOC_NR(cmd) > THEAD_IOC_MAXNR)
-        return -ENOTTY;
-
-    if (_IOC_DIR(cmd) & _IOC_READ)
-        err = !access_ok((void *) arg, _IOC_SIZE(cmd));
-    else if (_IOC_DIR(cmd) & _IOC_WRITE)
-        err = !access_ok((void *) arg, _IOC_SIZE(cmd));
-    if (err)
-        return -EFAULT;
-
-    switch (cmd)
-    {
-    case THEAD_IOCH_CONFIG_IVS:
-    {
-        struct ivs_parameter params;
-        printk("%s: THEAD_IOCH_CONFIG_IVS\n", __func__);
-        err = copy_from_user(&params, (struct ivs_parameter*)arg, sizeof(struct ivs_parameter));
-        config_ivs(filp, &params);
-        print_registers();
-        theadivs_data->state = THEADIVS_READY;
-        break;
-    }
-    case THEAD_IOCH_START_IVS:
-    {
-        printk("%s: THEAD_IOCH_START_IVS\n", __func__);
-        iowrite32(1, theadivs_data->hwregs + 0x34); // START
-        print_registers();
-        theadivs_data->state = THEADIVS_RUNNING;
-        break;
-    }
-    case THEAD_IOCH_RESET_IVS:
-    {
-        printk("%s: THEAD_IOCH_RESET_IVS\n", __func__);
-        iowrite32(1, theadivs_data->hwregs + 0x40); // INT_MASK
-        iowrite32(1, theadivs_data->hwregs + 0x28); // CLEAR
-        iowrite32(0, theadivs_data->hwregs + 0x40); // INT_MASK
-        print_registers();
-        theadivs_data->state = THEADIVS_IDLE;
-        break;
-    }
-    case THEAD_IOCH_GET_STATE:
-    {
-        printk("%s: THEAD_IOCH_GET_STATE: %d\n", __func__, theadivs_data->state);
-        err = copy_to_user((int *)arg, &theadivs_data->state, sizeof(int));
-        break;
-    }
-    default:
-    {
-        printk("%s: undefined command: 0x%x\n", __func__, cmd);
-    }
-    }
-    return 0;
-}
-
-static int theadivs_open(struct inode *inode, struct file *filp)
-{
-    int result = 0;
-    //theadivs_dev *dev = theadivs_data;
-    //filp->private_data = (void *) dev;
-
-    return result;
-}
-static int theadivs_release(struct inode *inode, struct file *filp)
-{
-    //theadivs_dev *dev = (theadivs_dev *) filp->private_data;
-
-    return 0;
-}
-
-static struct file_operations theadivs_fops = {
-    .owner= THIS_MODULE,
-    .open = theadivs_open,
-    .release = theadivs_release,
-    .unlocked_ioctl = theadivs_ioctl,
-    .fasync = NULL,
-};
-
-static const struct of_device_id thead_of_match[] = {
-        { .compatible = "thead,light-ivs",  },
-        { /* sentinel */  },
-};
-
-static int theadivs_reserve_IO(void)
-{
-    if(!request_mem_region
-        (theadivs_data->base_addr, theadivs_data->iosize, "shake"))
-    {
-        printk(KERN_INFO "theadivs: failed to reserve HW regs\n");
-        printk(KERN_INFO "theadivs: base_addr = 0x%08lx, iosize = %d\n",
-                          theadivs_data->base_addr,
-                          theadivs_data->iosize);
-        return -1;
-    }
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,17,0))
-    theadivs_data->hwregs =
-        (volatile u8 *) ioremap_nocache(theadivs_data->base_addr,
-                                        theadivs_data->iosize);
-#else
-    theadivs_data->hwregs =
-        (volatile u8 *) ioremap(theadivs_data->base_addr,
-                                        theadivs_data->iosize);
-#endif
-
-    if (theadivs_data->hwregs == NULL)
-    {
-        printk(KERN_INFO "theadivs: failed to ioremap HW regs\n");
-        release_mem_region(theadivs_data->base_addr, theadivs_data->iosize);
-        return -1;
-    }
-
-    printk("theadivs: mapped from 0x%lx to %p with size %d\n", 
-        theadivs_data->base_addr, theadivs_data->hwregs, theadivs_data->iosize);
-
-    return 0;
-}
-
-static void theadivs_release_IO(void)
-{
-    if(theadivs_data->hwregs)
-    {
-        iounmap((void *) theadivs_data->hwregs);
-        release_mem_region(theadivs_data->base_addr, theadivs_data->iosize);
-        theadivs_data->hwregs = NULL;
-    }
-}
-
-int __init theadivs_probe(struct platform_device *pdev)
-{
-    int result = -1;
-    struct resource *mem;
-    printk("enter %s\n",__func__);
-
-    theadivs_data = (theadivs_dev *)vmalloc(sizeof(theadivs_dev));
-    if (theadivs_data == NULL)
-        return result;
-    memset(theadivs_data, 0, sizeof(theadivs_dev));
-
-    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-    if(mem->start)
-        theadivs_data->base_addr = mem->start; 
-    theadivs_data->irq = platform_get_irq(pdev, 0);
-    printk("%s:get irq %d\n", __func__, theadivs_data->irq);
-    theadivs_data->iosize = IVS_SWREG_AMOUNT * 4;
-#if 1
-	if (device_register_index == 0)
-	{
-		if (theadivs_major == 0)
-		{
-			result = alloc_chrdev_region(&theadivs_data->devt, 0, 1, "shake");
-			if (result != 0)
-			{
-				printk("%s:alloc_chrdev_region error\n", __func__);
-				goto err1;
-			}
-			theadivs_major = MAJOR(theadivs_data->devt);
-			theadivs_minor = MINOR(theadivs_data->devt);
-		}
-		else
-		{
-			theadivs_data->devt = MKDEV(theadivs_major, theadivs_minor);
-			result = register_chrdev_region(theadivs_data->devt, 1, "shake");
-			if (result)
-			{
-				printk("%s:register_chrdev_region error\n", __func__);
-				goto err1;
-			}
-		}
-
-		theadivs_class = class_create(THIS_MODULE, "shake");
-		if (IS_ERR(theadivs_class))
-		{
-			printk("%s[%d]:class_create error!\n", __func__, __LINE__);
-			goto err;
-		}
-	}
-	theadivs_data->devt = MKDEV(theadivs_major, theadivs_minor + pdev->id);
-
-	cdev_init(&theadivs_data->cdev, &theadivs_fops);
-	result = cdev_add(&theadivs_data->cdev, theadivs_data->devt, 1);
-	if ( result )
-	{
-		printk("%s[%d]:cdev_add error!\n", __func__, __LINE__);
-		goto err;
-	}
-	theadivs_data->class = theadivs_class;
-	device_create(theadivs_data->class, NULL, theadivs_data->devt,
-			theadivs_data, "shake");
-
-	device_register_index++;
-#else
-    result = register_chrdev(theadivs_major, "shake", &theadivs_fops);
-    if (result < 0)
-    {
-        printk(KERN_INFO "theadivs_driver: unable to get major <%d>\n",
-              theadivs_major);
-        goto err1;
-    }
-    else if (result != 0)    /* this is for dynamic major */
-    {
-        theadivs_major = result;
-    }
-#endif
-
-    theadivs_reserve_IO();
-
-    /* get the IRQ line */
-    if (theadivs_data->irq!= -1)
-    {
-        result = request_irq(theadivs_data->irq, theadivs_isr,
-                              IRQF_SHARED,
-                              "shake", (void *)theadivs_data);
-        if (result == -EINVAL)
-        {
-            printk(KERN_ERR "theadivs_driver: Bad irq number or handler.\n");
-            theadivs_release_IO();
-            goto err;
-        }
-        else if (result == -EBUSY)
-        {
-            printk(KERN_ERR "theadivs_driver: IRQ <%d> busy, change your config.\n",
-                    theadivs_data->irq);
-            theadivs_release_IO();
-            goto err;
-        }
-    }
-    else
-    {
-        printk(KERN_INFO "theadivs_driver: IRQ not in use!\n");
-    }
-
-    printk(KERN_INFO "theadivs_driver: module inserted. Major <%d>\n", theadivs_major);
-
-    return 0;
-err:
-    //unregister_chrdev(theadivs_major, "shake");
-    unregister_chrdev_region(theadivs_data->devt, 1);
-err1:
-    if (theadivs_data != NULL)
-        vfree(theadivs_data);
-    printk(KERN_INFO "theadivs_driver: module not inserted\n");
-    return result;
-}
-
-static int theadivs_remove(struct platform_device *pdev)
-{
-    free_irq(theadivs_data->irq, theadivs_data);
-    theadivs_release_IO();
-    //unregister_chrdev(theadivs_major, "shake");
-    device_register_index--;
-	cdev_del(&theadivs_data->cdev);
-	device_destroy(theadivs_data->class, theadivs_data->devt);
-    unregister_chrdev_region(theadivs_data->devt, 1);
-	if (device_register_index == 0)
-	{
-		class_destroy(theadivs_data->class);
-	}
-    if (theadivs_data != NULL)
-        vfree(theadivs_data);
-
-    printk(KERN_INFO "theadivs_driver: module removed\n");
-    return 0;
-}
-
-
-static struct platform_driver theadivs_driver = {
-    .probe = theadivs_probe,
-    .remove = theadivs_remove,
-    .driver = {
-        .name = "shake",
-        .owner = THIS_MODULE,
-        .of_match_table = of_match_ptr(thead_of_match),
-    }
-};
-
-
-int __init theadivs_init(void)
-{
-    int ret = 0;
-    printk("enter %s\n",__func__);
-#if 1
-    ret = platform_driver_register(&theadivs_driver);
-    if (ret)
-    {
-        pr_err("register platform driver failed!\n");
-    }
-#endif
-    return ret;
-}
-
-void __exit theadivs_cleanup(void)
-{
-    printk("enter %s\n",__func__);
-    platform_driver_unregister(&theadivs_driver);
-    return;
-}
-
-static irqreturn_t theadivs_isr(int irq, void *dev_id)
-{
-    theadivs_dev *dev = (theadivs_dev *) dev_id;
-    u32 irq_status = 0;
-
-    printk( "theadivs_isr: received IRQ!\n");
-    irq_status = (u32)ioread32((void*)dev->hwregs + 0x38); // INT_STATE
-    printk( "INT_STATE of is: 0x%x\n", irq_status);
-    theadivs_data->state = THEADIVS_ERROR;
-
-    iowrite32(1, theadivs_data->hwregs + 0x40); // INT_MASK
-    iowrite32(1, theadivs_data->hwregs + 0x3c); // INT_CLEAN
-    iowrite32(1, theadivs_data->hwregs + 0x28); // CLEAR
-    iowrite32(0, theadivs_data->hwregs + 0x40); // INT_MASK
-
-    return IRQ_HANDLED;
-}
-
-
-module_init(theadivs_init);
-module_exit(theadivs_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("T-HEAD");
-MODULE_DESCRIPTION("T-HEAD ISP-VENC-SHAKE driver");
-

+ 0 - 71
kernel_mode/isp_venc_shake_driver.h

@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2021 - 2022  Alibaba Group. All rights reserved.
- * 
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-#ifndef _ISP_VENC_SHAKE_DRIVER_H_
-#define _ISP_VENC_SHAKE_DRIVER_H_
-#include <linux/ioctl.h>
-
-#define THEAD_IOC_MAGIC  't'
-
-#define THEAD_IOCH_CONFIG_IVS    _IOR(THEAD_IOC_MAGIC, 3, unsigned long *)
-#define THEAD_IOCH_START_IVS    _IOR(THEAD_IOC_MAGIC, 4, unsigned long *)
-#define THEAD_IOCH_RESET_IVS    _IOR(THEAD_IOC_MAGIC, 5, unsigned long *)
-#define THEAD_IOCH_GET_STATE    _IOR(THEAD_IOC_MAGIC, 6, int *)
-
-#define THEAD_IOC_MAXNR 6
-
-typedef int8_t    i8;
-typedef uint8_t   u8;
-typedef int16_t   i16;
-typedef uint16_t  u16;
-typedef int32_t   i32;
-typedef uint32_t  u32;
-typedef int64_t   i64;
-typedef uint64_t  u64;
-
-typedef enum _theadivs_state
-{
-    THEADIVS_IDLE,
-    THEADIVS_READY,
-    THEADIVS_RUNNING,
-    THEADIVS_ERROR
-} theadivs_state;
-
-struct ivs_parameter
-{
-  u32 pic_width;
-  u32 pic_height;
-  u32 encode_width;
-  u32 encode_height;
-  u32 wid_y;
-  u32 wid_uv;
-  u32 sram_size;
-  u32 encode_n;
-  u32 stride_y;
-  u32 stride_uv;
-  //u32 clear;
-  u32 encode_x;
-  u32 encode_y;
-  //u32 start;
-  //u32 int_state;
-  //u32 int_clean;
-  u32 int_mask;
-  //u32 signal_resv;
-};
-
-#endif /* !_ISP_VENC_SHAKE_DRIVER_H_ */

+ 552 - 0
src/process_linker.c

@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include "process_linker.h"
+
+#ifndef NULL
+#define NULL    ((void *)0)
+#endif
+
+#define MAX_CONNECTIONS 3
+#define MAX_BUFFER_SIZE (4 * 1024 * 1024)
+
+#define PLINK_PRINT(level, ...) \
+    { \
+        if (log_level >= PLINK_LOG_##level) \
+        { \
+            struct timeval ts; \
+            gettimeofday(&ts, 0); \
+            printf("PLINK[%d][%ld.%06ld] %s: ", pid, ts.tv_sec, ts.tv_usec, #level); \
+            printf(__VA_ARGS__); \
+        } \
+    }
+
+#define PLINK_PRINT_RETURN(retcode, level, ...) \
+    { \
+        PLINK_PRINT(level, __VA_ARGS__) \
+        return retcode; \
+    }
+
+typedef enum _PlinkLogLevel
+{
+    PLINK_LOG_QUIET = 0,
+    PLINK_LOG_ERROR,
+    PLINK_LOG_WARNING,
+    PLINK_LOG_INFO,
+    PLINK_LOG_DEBUG,
+    PLINK_LOG_TRACE,
+    PLINK_LOG_MAX
+} PlinkLogLevel;
+
+typedef struct _PlinkContext
+{
+    PlinkMode mode;
+    struct sockaddr_un addr;
+    struct iovec ioIn[PLINK_MAX_DATA_DESCS];
+    struct iovec ioOut[PLINK_MAX_DATA_DESCS];
+    int sockfd;
+    int cfd[MAX_CONNECTIONS];
+    int connect[MAX_CONNECTIONS];
+    int count; // connected client number
+    char *buffer;
+    int offset;
+    int pid;
+} PlinkContext;
+
+int log_level = PLINK_LOG_ERROR;
+int pid = 0;
+
+static PlinkStatus parseData(PlinkContext *ctx, PlinkPacket *pkt, int total);
+static PlinkStatus wait(int sockfd, int timeout_ms);
+static int getLogLevel();
+
+PlinkStatus
+PLINK_getVersion(PlinkVersion *version)
+{
+    if (version != NULL)
+    {
+        version->v.major = PLINK_VERSION_MAJOR;
+        version->v.minor = PLINK_VERSION_MINOR;
+        version->v.revision = PLINK_VERSION_REVISION;
+        version->v.step = 0;
+        return PLINK_STATUS_OK;
+    }
+    else
+        return PLINK_STATUS_ERROR;
+}
+
+PlinkStatus 
+PLINK_create(PlinkHandle *plink, const char *name, PlinkMode mode)
+{
+    PlinkContext *ctx = NULL;
+    int sockfd;
+
+    log_level = getLogLevel();
+    pid = getpid();
+
+    if (plink == NULL || name == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p, name = %s\n", plink, name);
+
+    ctx = (PlinkContext *)malloc(sizeof(*ctx));
+    if (NULL == ctx)
+        PLINK_PRINT_RETURN(PLINK_STATUS_NO_MEMORY, ERROR,
+            "Failed to allocate memory for plink\n");
+    memset(ctx, 0, sizeof(*ctx));
+    *plink = (PlinkHandle)ctx;
+
+    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (-1 == sockfd)
+        PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+            "Failed to create socket as AF_UNIX, SOCK_STREAM\n");
+
+    ctx->sockfd = sockfd;
+    ctx->mode = mode;
+    ctx->addr.sun_family = AF_UNIX;
+    strncpy(ctx->addr.sun_path, name, sizeof(ctx->addr.sun_path) - 1);
+
+    if (mode == PLINK_MODE_SERVER)
+    {
+        if (unlink (name) == -1 && errno != ENOENT)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+                "Failed to unlink %s\n", name);
+
+        if (bind(sockfd, (struct sockaddr *)&ctx->addr, sizeof(struct sockaddr_un)) == -1)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+                "Failed to bind to AF_UNIX address: %s\n", ctx->addr.sun_path);
+
+        if (listen(sockfd, MAX_CONNECTIONS) == -1)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+                "Failed to listen for connection request\n");
+    }
+
+    ctx->buffer = malloc(MAX_BUFFER_SIZE);
+    if (ctx->buffer == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_NO_MEMORY, ERROR,
+            "Failed to allocate memory for internal buffer\n");
+
+    ctx->pid = getpid();
+
+    return PLINK_STATUS_OK;
+}
+
+PlinkStatus 
+PLINK_connect(PlinkHandle plink, PlinkChannelID *channel)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p\n", plink);
+
+    if (ctx->mode == PLINK_MODE_SERVER)
+    {
+        if (channel == NULL)
+            PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+                "Wrong parameters: channel = %p\n", channel);
+
+        if (ctx->count >= MAX_CONNECTIONS)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+               "Too many connections %d while it is limited to %d\n", ctx->count, MAX_CONNECTIONS);
+
+        // find available slot
+        int i;
+        for (i = 0; i < MAX_CONNECTIONS; i++)
+        {
+            if (ctx->connect[i] == 0)
+            {
+                ctx->connect[i] = 1;
+                *channel = i;
+                break;
+            }
+        }
+
+        // wait for connection from client
+        PLINK_PRINT(INFO, "Waiting for connection...\n");
+        int fd = accept(ctx->sockfd, NULL, NULL);
+        if (fd == -1)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+               "Failed to accept connection\n");
+
+        ctx->cfd[i] = fd;
+        ctx->count++;
+        PLINK_PRINT(INFO, "Accepted connection request from client %d (%d/%d): %d\n", 
+                i, ctx->count, MAX_CONNECTIONS, fd);
+    }
+    else
+    {
+        if (connect(ctx->sockfd, (struct sockaddr *)&ctx->addr, sizeof(struct sockaddr_un)) == -1)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+               "Failed to connect to server %s: %s\n", ctx->addr.sun_path, strerror(errno));
+
+        PLINK_PRINT(INFO, "Connected to server: %d\n", ctx->sockfd);
+    }
+
+    return PLINK_STATUS_OK;
+}
+
+PlinkStatus 
+PLINK_send(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL || pkt == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p, pkt = %p\n", plink, pkt);
+
+    if (pkt->num > PLINK_MAX_DATA_DESCS)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Too many data nodes to send: %d\n", pkt->num);
+
+    char buf[CMSG_SPACE(sizeof(int))];
+    memset(buf, 0, sizeof(buf));
+    for (int i = 0; i < pkt->num; i++)
+    {
+        PlinkDescHdr *hdr = (PlinkDescHdr *)(pkt->list[i]);
+        ctx->ioOut[i].iov_base = pkt->list[i];
+        ctx->ioOut[i].iov_len = hdr->size + DATA_HEADER_SIZE;
+        PLINK_PRINT(INFO, "Sending Out %ld bytes\n", ctx->ioOut[i].iov_len);
+    }
+
+    struct msghdr msg = {0};
+    msg.msg_iov = ctx->ioOut;
+    msg.msg_iovlen = pkt->num;
+
+    if (pkt->fd > PLINK_INVALID_FD)
+    {
+        msg.msg_control = buf;
+        msg.msg_controllen = sizeof(buf);
+
+        struct cmsghdr *cmsg;
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        *((int *)CMSG_DATA(cmsg)) = pkt->fd;
+        PLINK_PRINT(INFO, "Sent fd %d\n", pkt->fd);
+    }
+
+    int sockfd = ctx->mode == PLINK_MODE_SERVER ? ctx->cfd[channel] : ctx->sockfd;
+    if (sendmsg(sockfd, &msg, 0) == -1)
+        PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+            "sendmsg() failed: %s\n", strerror(errno));
+    PLINK_PRINT(INFO, "Sent data to %d\n", sockfd);
+
+    return PLINK_STATUS_OK;
+}
+
+PlinkStatus 
+PLINK_recv(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL || pkt == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p, pkt = %p\n", plink, pkt);
+
+    pkt->num = 0;
+
+    char buf[CMSG_SPACE(sizeof(int))];
+    memset(buf, 0, sizeof(buf));
+    ctx->ioIn[0].iov_base = ctx->buffer + ctx->offset;
+    ctx->ioIn[0].iov_len = MAX_BUFFER_SIZE - ctx->offset;
+
+    struct msghdr msg = {0};
+    msg.msg_iov = ctx->ioIn;
+    msg.msg_iovlen = 1;
+    msg.msg_control = buf;
+    msg.msg_controllen = sizeof(buf);
+
+    int sockfd = ctx->mode == PLINK_MODE_SERVER ? ctx->cfd[channel] : ctx->sockfd;
+    PLINK_PRINT(INFO, "Receiving data from %d\n", sockfd);
+    int total = recvmsg (sockfd, &msg, 0);
+    if (total > 0)
+        PLINK_PRINT(INFO, "Received %d bytes\n", total)
+    else if (total == 0)
+        PLINK_PRINT_RETURN(PLINK_STATUS_NO_DATA, WARNING,
+            "recvmsg() returns %d\n", total)
+    else
+        PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+            "Failed to recieve data from %d: %s\n", sockfd, strerror(errno))
+
+    if (msg.msg_controllen >= CMSG_SPACE(sizeof(int)))
+    {
+        struct cmsghdr *cmsg;
+        cmsg = CMSG_FIRSTHDR(&msg);
+        pkt->fd = *((int *)CMSG_DATA(cmsg));
+        PLINK_PRINT(INFO, "Received fd %d\n", pkt->fd);
+    }
+    else
+        pkt->fd = PLINK_INVALID_FD;
+
+    return parseData(ctx, pkt, ctx->offset + total);
+}
+
+PlinkStatus 
+PLINK_wait(PlinkHandle plink, PlinkChannelID channel, int timeout_ms)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p\n", plink);
+
+    int sockfd = ctx->mode == PLINK_MODE_SERVER ? ctx->cfd[channel] : ctx->sockfd;
+    return wait(sockfd, timeout_ms);
+}
+
+PlinkStatus 
+PLINK_close(PlinkHandle plink, PlinkChannelID channel)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p\n", plink);
+
+    if (ctx->mode == PLINK_MODE_SERVER)
+    {
+        if (channel == PLINK_CLOSE_ALL)
+        {
+            // close all connections
+            int i;
+            for (i = 0; i < MAX_CONNECTIONS; i++)
+            {
+                if (ctx->connect[i] != 0)
+                {
+                    ctx->connect[i] = 0;
+                    close(ctx->cfd[i]);
+                    ctx->count--;
+                    PLINK_PRINT(INFO, "Closed channel %d\n", i);
+                }
+            }
+
+            unlink(ctx->addr.sun_path);
+            close(ctx->sockfd);
+            if (ctx->buffer != NULL)
+                free(ctx->buffer);
+
+            free(ctx);
+        }
+        else if (channel < MAX_CONNECTIONS && ctx->connect[channel] != 0)
+        {
+            close(ctx->cfd[channel]);
+            ctx->count--;
+            PLINK_PRINT(INFO, "Closed channel %d\n", channel);
+        }
+        else
+        {
+            PLINK_PRINT(ERROR, "Invalid channel: %d\n", channel);
+        }
+
+        unlink(ctx->addr.sun_path);
+    }
+    else
+    {
+        close(ctx->sockfd);
+        if (ctx->buffer != NULL)
+            free(ctx->buffer);
+
+        free(ctx);
+    }
+
+    return PLINK_STATUS_OK;
+}
+
+PlinkStatus
+PLINK_connect_ex(PlinkHandle plink, PlinkChannelID *channel, int timeout_ms)
+{
+    PlinkContext *ctx = (PlinkContext *)plink;
+
+    if (ctx == NULL)
+        PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+            "Wrong parameters: plink = %p\n", plink);
+
+    int i = 0;
+    if (ctx->mode == PLINK_MODE_SERVER)
+    {
+        if (channel == NULL)
+            PLINK_PRINT_RETURN(PLINK_STATUS_WRONG_PARAMS, ERROR,
+                "Wrong parameters: channel = %p\n", channel);
+
+        if (ctx->count >= MAX_CONNECTIONS)
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+               "Too many connections %d while it is limited to %d\n", ctx->count, MAX_CONNECTIONS);
+
+        // find available slot
+        for (i = 0; i < MAX_CONNECTIONS; i++)
+        {
+            if (ctx->connect[i] == 0)
+            {
+                ctx->connect[i] = 1;
+                *channel = i;
+                break;
+            }
+        }
+
+        // wait for connection from client
+        PLINK_PRINT(INFO, "Waiting for connection...\n");
+        if (wait(ctx->sockfd, timeout_ms) == PLINK_STATUS_OK)
+        {
+            int fd = accept(ctx->sockfd, NULL, NULL);
+            if (fd == -1)
+                PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+                    "Failed to accept connection\n");
+
+            ctx->cfd[i] = fd;
+            ctx->count++;
+            PLINK_PRINT(INFO, "Accepted connection request from client %d (%d/%d): %d\n", 
+                    i, ctx->count, MAX_CONNECTIONS, fd);
+        }
+        else
+            PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+                "No connection request within %dms\n", timeout_ms);
+    }
+    else
+    {
+        int max_tries = (timeout_ms + 1000 - 1) / 1000;
+        for (i = 0; i < max_tries; i++)
+        {
+            if (connect(ctx->sockfd, (struct sockaddr *)&ctx->addr, sizeof(struct sockaddr_un)) == -1)
+                sleep(1);
+            else
+                break;
+        }
+
+        if (i >= max_tries)
+            PLINK_PRINT_RETURN(PLINK_STATUS_TIMEOUT, WARNING,
+               "Failed to connect to server %s\n", ctx->addr.sun_path);
+
+        PLINK_PRINT(INFO, "Connected to server: %d\n", ctx->sockfd);
+    }
+
+    return PLINK_STATUS_OK;
+}
+
+PlinkStatus 
+PLINK_recv_ex(PlinkHandle plink, PlinkChannelID channel, PlinkPacket *pkt, int timeout_ms)
+{
+    int ret = PLINK_wait(plink, channel, timeout_ms);
+    if (ret == PLINK_STATUS_OK)
+    {
+        return PLINK_recv(plink, channel, pkt);
+    }
+
+    return ret;
+}
+
+
+static PlinkStatus 
+parseData(PlinkContext *ctx, PlinkPacket *pkt, int remaining)
+{
+    PlinkStatus sts = PLINK_STATUS_OK;
+
+    if (ctx == NULL)
+        return PLINK_STATUS_ERROR;
+
+    int index = 0;
+    void *buffer = ctx->buffer;
+    while (remaining >= DATA_HEADER_SIZE)
+    {
+        if (index >= PLINK_MAX_DATA_DESCS)
+        {
+            // not enough buffer to store received data, need another recv call
+            sts = PLINK_STATUS_MORE_DATA;
+            PLINK_PRINT(ERROR, "sts:%d Received %d bytes,index exceed max:%d!\n",
+			    sts, remaining, PLINK_MAX_DATA_DESCS);
+            break;
+        }
+
+        PlinkDescHdr *hdr = (PlinkDescHdr *)buffer;
+        if (remaining < (unsigned int)hdr->size)
+        {
+            PLINK_PRINT(WARNING, "Not enough data received. Expect %d while only %d available\n", hdr->size, remaining);
+            // return to get more data in next recvmsg call
+            break;
+        }
+
+        pkt->list[index] = buffer;
+
+        buffer += DATA_HEADER_SIZE + hdr->size;
+        remaining -= DATA_HEADER_SIZE + hdr->size;
+        index++;
+    }
+
+    // move the remaining data to the beginning of the buffer
+    if (remaining > 0)
+    {
+        memmove(ctx->buffer, buffer, remaining);
+        ctx->offset = remaining;
+    }
+
+    pkt->num = sts == PLINK_STATUS_MORE_DATA ? index-1 : index;
+
+    return sts;
+}
+
+static int getLogLevel()
+{
+    char *env = getenv("PLINK_LOG_LEVEL");
+    if (env == NULL)
+        return PLINK_LOG_ERROR;
+    else
+    {
+        int level = atoi(env);
+        if (level >= PLINK_LOG_MAX || level < PLINK_LOG_QUIET)
+            return PLINK_LOG_ERROR;
+        else
+            return level;
+    }
+}
+
+static PlinkStatus 
+wait(int sockfd, int timeout_ms)
+{
+    fd_set rfds;
+    struct timeval tv;
+    int ret;
+
+    FD_ZERO(&rfds);
+    FD_SET(sockfd, &rfds);
+
+    tv.tv_sec = timeout_ms / 1000;
+    tv.tv_usec = (timeout_ms % 1000) * 1000;
+    PLINK_PRINT(INFO, "Waiting for data from socket %d, timeout %dms\n", sockfd, timeout_ms);
+    ret = select(sockfd+1, &rfds, NULL, NULL, &tv);
+    if (ret == -1)
+        PLINK_PRINT_RETURN(PLINK_STATUS_ERROR, ERROR,
+            "Failed to wait for data\n")
+    else if (ret)
+        return PLINK_STATUS_OK;
+    else if (timeout_ms > 0)
+        PLINK_PRINT_RETURN(PLINK_STATUS_TIMEOUT, WARNING,
+            "Wait timeout\n");
+
+    return PLINK_STATUS_TIMEOUT;
+}

+ 0 - 73
test/Makefile

@@ -1,73 +0,0 @@
-
-CFLAGS = -Wall -D_GNU_SOURCE -D_REENTRANT -D_THREAD_SAFE -O2 -Werror -Wno-unused -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-strict-overflow -Wno-array-bounds -Wno-shift-negative-value -Wempty-body -Wtype-limits -Wno-unused-result -fPIC -Wmissing-field-initializers -std=gnu99
-
-INCLUDE += -I../user_mode
-
-SRCS = isp_venc_shake.c
-OBJS = $(SRCS:.c=.o)
-
-#source search path
-vpath %.c
-
-# name of the outputfile (library)
-IVSLIB = ../user_mode/libivs.a
-IVSSLIB = ../user_mode/libivs.so
-TARGET = ivs_test
-
-#Here are rules for building codes and generating object library.
-all:    tags
-	@echo ---------------------------------------
-	@echo "Usage: make [ system | testdata | versatile | integrator | android]"
-	@echo "system     - PC system model (== pclinux)"
-	@echo "testdata   - PC system model for test data creation"
-	@echo "eval       - PC system model for evaluation with frame limit"
-	@echo "versatile  - ARM versatile with FPGA HW"
-	@echo "integrator - ARM integrator with FPGA HW"
-	@echo "android    - ARM android"
-	@echo "NOTE! Make sure to do 'make clean'"
-	@echo "between compiling to different targets!"
-	@echo ---------------------------------------
-
-.PHONY: system_lib system testdata clean depend
-
-evaluation: eval
-eval: system
-
-system_static: testdata
-system: testdata
-
-# for libva
-system_lib: system
-
-testdata: .depend $(IVSLIB)
-
-
-.PHONY: hwlinux
-hwlinux:
-hwlinux: .depend $(TARGET)
-
-$(TARGET): $(OBJS)
-	$(CC) $(CFLAGS) $(OBJS) $(IVSLIB) -lm -lpthread -o $(TARGET)
-
-
-
-%.o: %.c
-	$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@
-
-clean:
-	$(RM) $(TARGET)
-	$(RM) .depend
-	$(RM) *.o *.gcno *.gcda
-
-tags:
-	ctags ../kernel_mode/*h ./*[ch]
-
-depend .depend: $(SRCS)
-	@echo "[CC] $@"
-	@$(CC) -M $(DEBFLAGS) $(INCLUDE) $^ > .depend
-
-ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
-ifeq (.depend, $(wildcard .depend))
-include .depend
-endif
-endif

+ 0 - 76
test/isp_venc_shake.c

@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
- * License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include "isp_venc_shake_hal.h"
-
-#ifndef NULL
-#define NULL    ((void *)0)
-#endif
-
-
-int main(void)
-{
-    void *ivs = NULL;
-    IvsConfig config;
-    IvsStatus status = IVS_STATUS_OK;
-
-    do
-    {
-        status = createIspVencShake(&ivs);
-        if (status != IVS_STATUS_OK)
-            break;
-
-        status = resetIspVencShake(ivs);
-        
-        config.mode = IVS_BUFFER_MODE_FRAME;
-        config.encode = IVS_ENCODER_FORMAT_H264;
-        config.pic_width = 640;
-        config.pic_height = 480;
-        config.encode_width = 640;
-        config.encode_height = 480;
-        config.buffer_height = 240;
-        config.stride_y = 640;
-        config.stride_uv = 640;
-        config.encode_x = 0;
-        config.encode_y = 0;
-        status = configIspVencShake(ivs, &config);
-        if (status != IVS_STATUS_OK)
-            break;
-        
-        status = startIspVencShake(ivs);
-        if (status != IVS_STATUS_OK)
-            break;
-
-        sleep(90);
-
-        status = resetIspVencShake(ivs);
-        if (status != IVS_STATUS_OK)
-            break;
-    } while (0);
-    
-    destroyIspVencShake(ivs);
-
-    if (status != IVS_STATUS_OK)
-        printf("ERROR: ivs_test exit with error!\n");
-    else
-        printf("ivs_test done!\n");
-
-    return (status == IVS_STATUS_OK ? 0 : 1);
-}
-

+ 203 - 0
test/plink_client.c

@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2021 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <memory.h>
+#include "process_linker_types.h"
+#include "video_mem.h"
+
+#ifndef NULL
+#define NULL    ((void *)0)
+#endif
+
+#define NUM_OF_BUFFERS  5
+#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
+                        } while (0)
+
+int main(int argc, char **argv) {
+    PlinkStatus sts = PLINK_STATUS_OK;
+    PlinkPacket sendpkt, recvpkt;
+    PlinkMsg msg;
+    PlinkHandle plink = NULL;
+    VmemParams params;
+    void *vmem = NULL;
+    FILE *fp = NULL;
+    int exitcode = 0;
+
+    int frames = argc > 1 ? atoi(argv[1]) : 1000;
+    char *plinkname = argc > 2 ? argv[2] : "/tmp/plink.test";
+    char *dumpname = argc > 3 ? argv[3] : NULL;
+
+    if (dumpname != NULL)
+    {
+        fp = fopen(dumpname, "wb");
+        if (fp == NULL)
+            errExit("fopen");
+    }
+
+    if (VMEM_create(&vmem) != VMEM_STATUS_OK)
+        errExit("Failed to create VMEM.");
+
+    if (PLINK_create(&plink, plinkname, PLINK_MODE_CLIENT) != PLINK_STATUS_OK)
+        errExit("Failed to create PLINK.");
+
+    if (PLINK_connect(plink, NULL) != PLINK_STATUS_OK)
+        errExit("Failed to connect to server.");
+
+    int frmcnt = 0;
+    do {
+        sts = PLINK_recv(plink, 0, &recvpkt);
+        memset(&params, 0, sizeof(params));
+        if (recvpkt.fd != PLINK_INVALID_FD)
+        {
+            params.fd = recvpkt.fd;
+            if (VMEM_import(vmem, &params) != VMEM_STATUS_OK)
+                errExit("Failed to import fd.");
+            if (VMEM_mmap(vmem, &params) != VMEM_STATUS_OK)
+                errExit("Failed to mmap buffer.");
+        }
+
+        for (int i = 0; i < recvpkt.num; i++)
+        {
+            PlinkDescHdr *hdr = (PlinkDescHdr *)(recvpkt.list[i]);
+            if (hdr->type == PLINK_TYPE_2D_YUV)
+            {
+                PlinkYuvInfo *pic = (PlinkYuvInfo *)(recvpkt.list[i]);
+                printf("[CLIENT] Received YUV frame %d 0x%010llx: fd %d, %dx%d, stride = luma %d, chroma %d\n", 
+                        pic->header.id, pic->bus_address_y, recvpkt.fd,
+                        pic->pic_width, pic->pic_height,
+                        pic->stride_y, pic->stride_u);
+
+                // Save YUV data to file
+                if (fp != NULL && params.vir_address != NULL)
+                {
+                    void *buffer = params.vir_address;
+                    for (int i = 0; i < pic->pic_height * 3 / 2; i++)
+                    {
+                        fwrite(buffer, pic->pic_width, 1, fp);
+                        buffer += pic->stride_y;
+                    }
+                }
+
+                // return the buffer to source
+                msg.header.type = PLINK_TYPE_MESSAGE;
+                msg.header.size = DATA_SIZE(PlinkMsg);
+                msg.msg = hdr->id;
+                sendpkt.list[0] = &msg;
+                sendpkt.num = 1;
+                sendpkt.fd = PLINK_INVALID_FD;
+                if (PLINK_send(plink, 0, &sendpkt) == PLINK_STATUS_ERROR)
+                    errExit("Failed to send data.");
+            }
+            if (hdr->type == PLINK_TYPE_2D_RGB)
+            {
+                PlinkRGBInfo *pic = (PlinkRGBInfo *)(recvpkt.list[i]);
+                printf("[CLIENT] Received RGB picture %d 0x%010llx: fd %d, %dx%d, stride %d/%d/%d/%d\n", 
+                        pic->header.id, pic->bus_address_r, recvpkt.fd,
+                        pic->img_width, pic->img_height,
+                        pic->stride_r, pic->stride_g, pic->stride_b, pic->stride_a);
+
+                // Save RGB data to file
+                if (fp != NULL && params.vir_address != NULL && pic->format == PLINK_COLOR_Format24BitBGR888Planar)
+                {
+                    void *buffer = params.vir_address;
+                    for (int i = 0; i < pic->img_height * 3; i++)
+                    {
+                        fwrite(buffer, pic->img_width, 1, fp);
+                        buffer += pic->stride_r;
+                    }
+                }
+
+                // return the buffer to source
+                msg.header.type = PLINK_TYPE_MESSAGE;
+                msg.header.size = DATA_SIZE(PlinkMsg);
+                msg.msg = hdr->id;
+                sendpkt.list[0] = &msg;
+                sendpkt.num = 1;
+                sendpkt.fd = PLINK_INVALID_FD;
+                if (PLINK_send(plink, 0, &sendpkt) == PLINK_STATUS_ERROR)
+                    errExit("Failed to send data.");
+            }
+            if (hdr->type == PLINK_TYPE_2D_RAW)
+            {
+                PlinkRawInfo *pic = (PlinkRawInfo *)(recvpkt.list[i]);
+                printf("[CLIENT] Received RAW picture %d 0x%010llx: fd %d, %dx%d, stride %d\n", 
+                        pic->header.id, pic->bus_address, recvpkt.fd,
+                        pic->img_width, pic->img_height, pic->stride);
+
+                // Save RAW data to file
+                if (fp != NULL && params.vir_address != NULL)
+                    fwrite(params.vir_address, pic->stride * pic->img_height, 1, fp);
+
+                // return the buffer to source
+                msg.header.type = PLINK_TYPE_MESSAGE;
+                msg.header.size = DATA_SIZE(PlinkMsg);
+                msg.msg = hdr->id;
+                sendpkt.list[0] = &msg;
+                sendpkt.num = 1;
+                sendpkt.fd = PLINK_INVALID_FD;
+                if (PLINK_send(plink, 0, &sendpkt) == PLINK_STATUS_ERROR)
+                    errExit("Failed to send data.");
+            }
+            else if (hdr->type == PLINK_TYPE_MESSAGE)
+            {
+                PlinkMsg *msg = (PlinkMsg *)(recvpkt.list[i]);
+                if (msg->msg == PLINK_EXIT_CODE)
+                {
+                    exitcode = 1;
+                    printf("Exit\n");
+                    break;
+                }
+            }
+        }
+
+        if (VMEM_release(vmem, &params) != VMEM_STATUS_OK)
+            errExit("Failed to release buffer.");
+
+        if (recvpkt.fd != PLINK_INVALID_FD)
+            close(recvpkt.fd);
+
+        frmcnt++;
+
+        if (frmcnt >= frames)
+        {
+            msg.header.type = PLINK_TYPE_MESSAGE;
+            msg.header.size = DATA_SIZE(PlinkMsg);
+            msg.msg = PLINK_EXIT_CODE;
+            sendpkt.list[0] = &msg;
+            sendpkt.num = 1;
+            sendpkt.fd = PLINK_INVALID_FD;
+            if (PLINK_send(plink, 0, &sendpkt) == PLINK_STATUS_ERROR)
+                errExit("Failed to send data.");
+            break;
+        }
+    } while (exitcode == 0);
+
+cleanup:
+    sleep(1); // Sleep one second to make sure server is ready for exit
+    PLINK_close(plink, 0);
+    VMEM_destroy(vmem);
+    if (fp != NULL)
+        fclose(fp);
+    exit(EXIT_SUCCESS);
+}

+ 432 - 0
test/plink_server.c

@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2021 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <memory.h>
+#include <errno.h>
+#include "process_linker_types.h"
+#include "video_mem.h"
+
+#ifndef NULL
+#define NULL    ((void *)0)
+#endif
+
+#define NUM_OF_BUFFERS  5
+#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
+                        } while (0)
+
+typedef struct _ServerParams
+{
+    char *plinkname;
+    char *inputfile;
+    PlinkColorFormat format;
+    int width;
+    int height;
+    int stride;
+    int frames;
+} ServerParams;
+
+typedef struct _PlinkChannel
+{
+    PlinkChannelID id;
+    PlinkHandle plink;
+    PlinkPacket pkt;
+    int sendid;
+    int backid;
+    int exit;
+    int available_bufs;
+} PlinkChannel;
+
+typedef struct _PictureBuffer
+{
+  unsigned int bus_address;
+  void *virtual_address;
+  unsigned int size;
+  int fd;
+} PictureBuffer;
+
+void printUsage(char *name)
+{
+    printf("usage: %s [options]\n"
+           "\n"
+           "  Available options:\n"
+           "    -l      plink file name (default: /tmp/plink.test)\n"
+           "    -i      input YUV file name (mandatory)\n"
+           "    -f      input color format (default: 3)\n"
+           "                2 - I420\n"
+           "                3 - NV12\n"
+           "                4 - P010\n"
+           "                14 - Bayer Raw 10bit\n"
+           "                15 - Bayer Raw 12bit\n"
+           "    -w      video width (mandatory)\n"
+           "    -h      video height (mandatory)\n"
+           "    -s      video buffer stride in bytes (default: video width)\n"
+           "    -n      number of frames to send (default: 10)\n"
+           "\n", name);
+}
+
+void parseParams(int argc, char **argv, ServerParams *params)
+{
+    int i = 1;
+    memset(params, 0, sizeof(*params));
+    params->plinkname = "/tmp/plink.test";
+    params->format = PLINK_COLOR_FormatYUV420SemiPlanar;
+    params->frames = 10;
+    while (i < argc)
+    {
+        if (argv[i][0] != '-' || strlen(argv[i]) < 2)
+        {
+            i++;
+            continue;
+        }
+
+        if (argv[i][1] == 'l')
+        {
+            if (++i < argc)
+            {
+                params->plinkname = argv[i++];
+            }
+        }
+        else if (argv[i][1] == 'i')
+        {
+            if (++i < argc)
+            {
+                params->inputfile = argv[i++];
+            }
+        }
+        else if (argv[i][1] == 'f')
+        {
+            if (++i < argc)
+            {
+                params->format = atoi(argv[i++]);
+            }
+        }
+        else if (argv[i][1] == 'w')
+        {
+            if (++i < argc)
+            {
+                params->width = atoi(argv[i++]);
+            }
+        }
+        else if (argv[i][1] == 'h')
+        {
+            if (++i < argc)
+            {
+                params->height = atoi(argv[i++]);
+            }
+        }
+        else if (argv[i][1] == 's')
+        {
+            if (++i < argc)
+            {
+                params->stride = atoi(argv[i++]);
+            }
+        }
+        else if (argv[i][1] == 'n')
+        {
+            if (++i < argc)
+            {
+                params->frames = atoi(argv[i++]);
+            }
+        }
+    }
+
+    if ((params->format == PLINK_COLOR_FormatYUV420SemiPlanarP010 ||
+            params->format == PLINK_COLOR_FormatRawBayer10bit ||
+            params->format == PLINK_COLOR_FormatRawBayer12bit) &&
+        params->stride < params->width*2)
+    {
+        params->stride = params->width*2;
+    }
+    else if (params->stride < params->width)
+    {
+        params->stride = params->width;
+    }
+}
+
+int checkParams(ServerParams *params)
+{
+    if (params->plinkname == NULL ||
+        params->inputfile == NULL ||
+        params->format == PLINK_COLOR_FormatUnused ||
+        params->width == 0 ||
+        params->height == 0 ||
+        params->stride == 0)
+        return -1;
+    return 0;
+}
+
+int getBufferSize(ServerParams *params)
+{
+    int size = 0;
+    switch (params->format)
+    {
+        case PLINK_COLOR_FormatYUV420Planar:
+        case PLINK_COLOR_FormatYUV420SemiPlanar:
+            size = params->stride * params->height * 3 / 2;
+            break;
+        case PLINK_COLOR_FormatYUV420SemiPlanarP010:
+            size = params->stride * params->height * 3;
+            break;
+        case PLINK_COLOR_FormatRawBayer10bit:
+        case PLINK_COLOR_FormatRawBayer12bit:
+            size = params->stride * params->height;
+            break;
+        default:
+            size = 0;
+    }
+    return size;
+}
+
+void AllocateBuffers(PictureBuffer picbuffers[NUM_OF_BUFFERS], unsigned int size, void *vmem)
+{
+    unsigned int buffer_size = (size + 0xFFF) & ~0xFFF;
+    VmemParams params;
+    params.size = buffer_size;
+    params.flags = VMEM_FLAG_CONTIGUOUS | VMEM_FLAG_4GB_ADDR;
+    for (int i = 0; i < NUM_OF_BUFFERS; i++)
+    {
+        params.fd = 0;
+        VMEM_allocate(vmem, &params);
+        VMEM_mmap(vmem, &params);
+        VMEM_export(vmem, &params);
+        printf("[SERVER] mmap %p from %x with size %d, dma-buf fd %d\n", 
+                params.vir_address, params.phy_address, params.size, params.fd);
+        picbuffers[i].virtual_address = params.vir_address;
+        picbuffers[i].bus_address = params.phy_address;
+        picbuffers[i].size = buffer_size;
+        picbuffers[i].fd = params.fd;
+    }
+}
+
+void FreeBuffers(PictureBuffer picbuffers[NUM_OF_BUFFERS], void *vmem)
+{
+    VmemParams params;
+    memset(&params, 0, sizeof(params));
+    for (int i = 0; i < NUM_OF_BUFFERS; i++)
+    {
+        close(picbuffers[i].fd);
+        params.size = picbuffers[i].size;
+        params.vir_address = picbuffers[i].virtual_address;
+        params.phy_address = picbuffers[i].bus_address;
+        VMEM_free(vmem, &params);
+    }
+}
+
+void ProcessOneFrame(void *virtual_address, FILE *fp, int size)
+{
+    if(virtual_address != MAP_FAILED && fp != NULL)
+    {
+        if (fread(virtual_address, size, 1, fp) < 1)
+            fprintf(stderr, "ERROR: Failed to read %d bytes from file (eof %d): %s\n",
+                size, feof(fp), strerror(errno));
+    }
+}
+
+void constructYuvInfo(PlinkYuvInfo *info, ServerParams *params, unsigned int bus_address, int id)
+{
+    int size_y = params->width * params->stride;
+    int size_uv = size_y / 2;
+    int size_yuv = size_y + size_uv;
+    
+    info->header.type = PLINK_TYPE_2D_YUV;
+    info->header.size = DATA_SIZE(*info);
+    info->header.id = id + 1;
+
+    info->format = params->format;
+    info->bus_address_y = bus_address;
+    info->bus_address_u = info->bus_address_y + size_y;
+    info->bus_address_v = info->bus_address_u + (size_uv / 2);
+    info->pic_width = params->width;
+    info->pic_height = params->height;
+    info->stride_y = params->stride;
+    info->stride_u = 
+        params->format == PLINK_COLOR_FormatYUV420Planar ? 
+        params->stride/2 : params->stride;
+    info->stride_v = info->stride_u;
+}
+
+void constructRawInfo(PlinkRawInfo *info, ServerParams *params, unsigned int bus_address, int id)
+{
+    int size_y = params->width * params->stride;
+    int size_uv = size_y / 2;
+    int size_yuv = size_y + size_uv;
+    
+    info->header.type = PLINK_TYPE_2D_RAW;
+    info->header.size = DATA_SIZE(*info);
+    info->header.id = id + 1;
+
+    info->format = params->format;
+    info->bus_address = bus_address;
+    info->img_width = params->width;
+    info->img_height = params->height;
+    info->stride = params->stride;
+}
+
+int getBufferCount(PlinkPacket *pkt)
+{
+    int ret = 0;
+    for (int i = 0; i < pkt->num; i++)
+    {
+        PlinkDescHdr *hdr = (PlinkDescHdr *)(pkt->list[i]);
+        if (hdr->type == PLINK_TYPE_MESSAGE)
+        {
+            int *data = (int *)(pkt->list[i] + DATA_HEADER_SIZE);
+            if (*data == PLINK_EXIT_CODE)
+            {
+                ret |= 0x80000000; // set bit 31 to 1 to indicate 'exit'
+            }
+            else if (*data >= 0)
+                ret++;
+        }
+    }
+
+    return ret;
+}
+
+void retreiveSentBuffers(PlinkHandle plink, PlinkChannel *channel)
+{
+    PlinkStatus sts = PLINK_STATUS_OK;
+    while (channel->available_bufs < NUM_OF_BUFFERS && sts == PLINK_STATUS_OK)
+    {
+        do
+        {
+            sts = PLINK_recv(plink, channel->id, &channel->pkt);
+            int count = getBufferCount(&channel->pkt);
+            if (count > 0)
+            {
+                channel->available_bufs += count;
+            }
+        } while (sts == PLINK_STATUS_MORE_DATA);
+    }
+}
+
+
+int main(int argc, char **argv) {
+    PlinkStatus sts = PLINK_STATUS_OK;
+    ServerParams params;
+    PlinkChannel channel[2];
+    PlinkHandle plink = NULL;
+    PlinkYuvInfo pic = {0};
+    PlinkRawInfo img = {0};
+    PlinkMsg msg;
+
+    parseParams(argc, argv, &params);
+    if (checkParams(&params) != 0)
+    {
+        printUsage(argv[0]);
+        return 0;
+    }
+
+    FILE *fp = fopen(params.inputfile, "rb");
+    if (fp == NULL)
+        errExit("fopen");
+
+    void *vmem = NULL;
+    if (VMEM_create(&vmem) != VMEM_STATUS_OK)
+        errExit("Failed to create VMEM.");
+
+    int width = params.width;
+    int height = params.height;
+    int stride = params.stride;
+    int frames = params.frames;
+    int size = getBufferSize(&params);
+    if (size == 0)
+        errExit("Wrong format or wrong resolution.");
+    PictureBuffer picbuffers[NUM_OF_BUFFERS];
+    AllocateBuffers(picbuffers, size, vmem);
+
+    sts = PLINK_create(&plink, params.plinkname, PLINK_MODE_SERVER);
+
+    memset(&channel[0], 0, sizeof(channel[0]));
+    channel[0].available_bufs = NUM_OF_BUFFERS;
+    sts = PLINK_connect(plink, &channel[0].id);
+
+    int frmcnt = 0;
+    do {
+        int sendid = channel[0].sendid;
+        ProcessOneFrame(picbuffers[sendid].virtual_address, fp, size);
+        if (params.format == PLINK_COLOR_FormatRawBayer8bit ||
+            params.format == PLINK_COLOR_FormatRawBayer10bit ||
+            params.format == PLINK_COLOR_FormatRawBayer12bit)
+        {
+            constructRawInfo(&img, &params, picbuffers[sendid].bus_address, sendid);
+            printf("[SERVER] Processed frame %d 0x%010llx: %dx%d, stride %d\n", 
+                    sendid, img.bus_address, img.img_width, img.img_height, img.stride);
+            channel[0].pkt.list[0] = &img;
+        }
+        else // YUV
+        {
+            constructYuvInfo(&pic, &params, picbuffers[sendid].bus_address, sendid);
+            printf("[SERVER] Processed frame %d 0x%010llx: %dx%d, stride = luma %d, chroma %d\n", 
+                    sendid, pic.bus_address_y, 
+                    pic.pic_width, pic.pic_height,
+                    pic.stride_y, pic.stride_u);
+            channel[0].pkt.list[0] = &pic;
+        }
+
+        channel[0].pkt.num = 1;
+        channel[0].pkt.fd = picbuffers[sendid].fd;
+        sts = PLINK_send(plink, channel[0].id, &channel[0].pkt);
+        channel[0].sendid = (channel[0].sendid + 1) % NUM_OF_BUFFERS;
+        channel[0].available_bufs -= 1;
+
+        int timeout = 0;
+        if (channel[0].available_bufs == 0)
+            timeout = 60000; // wait up to 60 seconds if buffers are used up
+
+        if (PLINK_wait(plink, channel[0].id, timeout) == PLINK_STATUS_OK)
+        {
+            do
+            {
+                sts = PLINK_recv(plink, channel[0].id, &channel[0].pkt);
+                int count = getBufferCount(&channel[0].pkt);
+                if (count < 0)
+                    channel[0].exit = 1;
+                channel[0].available_bufs += count;
+            } while (sts == PLINK_STATUS_MORE_DATA);
+        }
+
+        frmcnt++;
+    } while (channel[0].exit == 0 && frmcnt < frames);
+
+cleanup:
+    msg.header.type = PLINK_TYPE_MESSAGE;
+    msg.header.size = DATA_SIZE(PlinkMsg);
+    msg.msg = PLINK_EXIT_CODE;
+    channel[0].pkt.list[0] = &msg;
+    channel[0].pkt.num = 1;
+    channel[0].pkt.fd = PLINK_INVALID_FD;
+    sts = PLINK_send(plink, channel[0].id, &channel[0].pkt);
+    retreiveSentBuffers(plink, &channel[0]);
+    PLINK_recv_ex(plink, channel[0].id, &channel[0].pkt, 1000);
+    //sleep(1); // Sleep one second to make sure client is ready for exit
+    if (vmem)
+        FreeBuffers(picbuffers, vmem);
+    PLINK_close(plink, PLINK_CLOSE_ALL);
+    VMEM_destroy(vmem);
+    if (fp != NULL)
+        fclose(fp);
+    exit(EXIT_SUCCESS);
+}
+

+ 766 - 0
test/plink_stitcher.c

@@ -0,0 +1,766 @@
+/*
+ * Copyright (c) 2022 Alibaba Group. All rights reserved.
+ * License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <memory.h>
+#include "process_linker_types.h"
+#include "video_mem.h"
+
+#ifndef NULL
+#define NULL    ((void *)0)
+#endif
+
+#define MAX_NUM_OF_INPUTS   4
+#define NUM_OF_BUFFERS      5
+#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
+                        } while (0)
+#define STITCHER_MIN(a, b) ((a) < (b) ? (a) : (b))
+
+
+typedef enum _StitchLayout
+{
+    STITCH_LAYOUT_Vertical = 0,
+    STITCH_LAYOUT_Horizontal,
+    STITCH_LAYOUT_Matrix,
+    STITCH_LAYOUT_Max
+} StitchLayout;
+
+typedef struct _StitherRegion
+{
+    int width;
+    int height;
+    int offset_uv;
+    int offset_y;
+} StitcherRegion;
+
+typedef struct _StitcherParams
+{
+    char *in_name[MAX_NUM_OF_INPUTS];
+    char *out_name;
+    StitchLayout layout;
+    PlinkColorFormat format;
+    int width;
+    int height;
+    int stride;
+} StitcherParams;
+
+typedef struct _StitcherPort
+{
+    char *name;
+    int index;
+    PlinkChannelID id;
+    PlinkHandle plink;
+    int sendid;
+    int backid;
+    int *exit;
+    int available_bufs;
+    void *vmem;
+    pthread_mutex_t pic_mutex;
+    pthread_mutex_t *count_mutex;
+    sem_t *sem_ready;
+    sem_t *sem_done;
+    StitchLayout layout;
+    PlinkColorFormat format;
+    void *buffer;
+    int width;
+    int height;
+    int stride;
+    int offset;
+    int offset_uv;
+    int *in_count;
+} StitcherPort;
+
+typedef struct _StitcherContext
+{
+    StitcherPort in[MAX_NUM_OF_INPUTS];
+    StitcherPort out;
+    pthread_mutex_t count_mutex;
+    sem_t sem_ready;
+    sem_t sem_done;
+    int in_count;
+    int exitcode;
+} StitcherContext;
+
+typedef struct _PictureBuffer
+{
+  unsigned int bus_address;
+  void *virtual_address;
+  unsigned int size;
+  int fd;
+} PictureBuffer;
+
+static void printUsage(char *name)
+{
+    printf("usage: %s [options]\n"
+           "\n"
+           "  Stitch multiple pictures to one. Maximum # of pictures to be stitched is %d\n"
+           "  Available options:\n"
+           "    -i<n>   plink file name of input port #n (default: /tmp/plink.stitch.in<n>). n is 0 based.\n"
+           "    -o      plink file name of output port (default: /tmp/plink.stitch.out)\n"
+           "    -l      layout (default: 0)\n"
+           "                0 - vertical\n"
+           "                1 - horizontal\n"
+           "                2 - matrix\n"
+           "    -f      output color format (default: 3)\n"
+           "                3 - NV12\n"
+           "    -w      output video width (default: 800)\n"
+           "    -h      output video height (default: 1280)\n"
+           "    -s      output video buffer stride (default: video width)\n"
+           "    --help  print this message\n"
+           "\n", name, MAX_NUM_OF_INPUTS);
+}
+
+static void parseParams(int argc, char **argv, StitcherParams *params)
+{
+    int i = 1;
+    memset(params, 0, sizeof(*params));
+    params->in_name[0] = "/tmp/plink.stitch.in0";
+    params->in_name[1] = "/tmp/plink.stitch.in1";
+    params->in_name[2] = "/tmp/plink.stitch.in2";
+    params->in_name[3] = "/tmp/plink.stitch.in3";
+    params->out_name = "/tmp/plink.stitch.out";
+    params->layout = STITCH_LAYOUT_Vertical;
+    params->format = PLINK_COLOR_FormatYUV420SemiPlanar;
+    params->width = 800;
+    params->height = 1280;
+    while (i < argc)
+    {
+        if (argv[i][0] != '-' || strlen(argv[i]) < 2)
+        {
+            i++;
+            continue;
+        }
+        
+        if (argv[i][1] == 'i')
+        {
+            if (++i < argc)
+            {
+                if (strlen(argv[i-1]) > 2) // input name
+                {
+                    int id = atoi(argv[i-1]+2);
+                    if (id < MAX_NUM_OF_INPUTS)
+                        params->in_name[id] = argv[i++];
+                }
+                else
+                    params->in_name[0] = argv[i++];
+            }
+        }
+        else if (argv[i][1] == 'o')
+        {
+            if (++i < argc)
+                params->out_name = argv[i++];
+        }
+        else if (argv[i][1] == 'l')
+        {
+            if (++i < argc)
+                params->layout = atoi(argv[i++]);
+        }
+        else if (argv[i][1] == 'f')
+        {
+            if (++i < argc)
+                params->format = atoi(argv[i++]);
+        }
+        else if (argv[i][1] == 'w')
+        {
+            if (++i < argc)
+                params->width = atoi(argv[i++]);
+        }
+        else if (argv[i][1] == 'h')
+        {
+            if (++i < argc)
+                params->height = atoi(argv[i++]);
+        }
+        else if (argv[i][1] == 's')
+        {
+            if (++i < argc)
+                params->stride = atoi(argv[i++]);
+        }
+        else if (strcmp(argv[i], "--help") == 0)
+        {
+            params->layout = STITCH_LAYOUT_Max;
+            i++;
+        }
+    }
+
+    if (params->stride == 0)
+        params->stride = params->width;
+
+    printf("[STITCHER] Input 0 Name         : %s\n", params->in_name[0]);
+    printf("[STITCHER] Input 1 Name         : %s\n", params->in_name[1]);
+    printf("[STITCHER] Input 2 Name         : %s\n", params->in_name[2]);
+    printf("[STITCHER] Input 3 Name         : %s\n", params->in_name[3]);
+    printf("[STITCHER] Output Name          : %s\n", params->out_name);
+    printf("[STITCHER] Output Layout        : %d\n", params->layout);
+    printf("[STITCHER] Output Format        : %d\n", params->format);
+    printf("[STITCHER] Output Resolution    : %dx%d\n", params->width, params->height);
+    printf("[STITCHER] Output Stride        : %d\n", params->stride);
+}
+
+static int checkParams(StitcherParams *params)
+{
+    if (params->format != PLINK_COLOR_FormatYUV420SemiPlanar ||
+        params->layout >= STITCH_LAYOUT_Max)
+        return -1;
+    return 0;
+}
+
+static int getBufferSize(StitcherParams *params)
+{
+    int size = 0;
+    switch (params->format)
+    {
+        case PLINK_COLOR_FormatYUV420Planar:
+        case PLINK_COLOR_FormatYUV420SemiPlanar:
+            size = params->stride * params->height * 3 / 2;
+            break;
+        default:
+            size = 0;
+    }
+    return size;
+}
+
+static void AllocateBuffers(PictureBuffer picbuffers[NUM_OF_BUFFERS], unsigned int size, void *vmem)
+{
+    unsigned int buffer_size = (size + 0xFFF) & ~0xFFF;
+    VmemParams params;
+    params.size = buffer_size;
+    params.flags = VMEM_FLAG_CONTIGUOUS | VMEM_FLAG_4GB_ADDR;
+    for (int i = 0; i < NUM_OF_BUFFERS; i++)
+    {
+        params.fd = 0;
+        VMEM_allocate(vmem, &params);
+        VMEM_mmap(vmem, &params);
+        VMEM_export(vmem, &params);
+        printf("[STITCHER] mmap %p from %x with size %d, dma-buf fd %d\n", 
+                params.vir_address, params.phy_address, params.size, params.fd);
+        picbuffers[i].virtual_address = params.vir_address;
+        picbuffers[i].bus_address = params.phy_address;
+        picbuffers[i].size = buffer_size;
+        picbuffers[i].fd = params.fd;
+    }
+}
+
+static void FreeBuffers(PictureBuffer picbuffers[NUM_OF_BUFFERS], void *vmem)
+{
+    VmemParams params;
+    memset(&params, 0, sizeof(params));
+    for (int i = 0; i < NUM_OF_BUFFERS; i++)
+    {
+        close(picbuffers[i].fd);
+        params.size = picbuffers[i].size;
+        params.vir_address = picbuffers[i].virtual_address;
+        params.phy_address = picbuffers[i].bus_address;
+        VMEM_free(vmem, &params);
+    }
+}
+
+static void getRegion(StitcherPort *out, int index, int in_count, StitcherRegion *region)
+{
+    region->offset_uv = 0; //out->stride * out->height;
+    if (out->layout == STITCH_LAYOUT_Horizontal)
+    {
+        int width = (out->width / in_count + 1) & ~1;
+        region->offset_y = width * index;
+        region->offset_uv += width * index;
+        region->width = width;
+        region->height = out->height;
+    }
+    else if (out->layout == STITCH_LAYOUT_Matrix && in_count >= 3)
+    {
+        int y = index >> 1;
+        int x = index & 1;
+        int width = (out->width / 2 + 1) & ~1;
+        int height = (out->height / 2 + 1) & ~1;
+        region->offset_y = out->stride * (height * y);
+        region->offset_y += width * x;
+        region->offset_uv += out->stride * (height * y / 2);
+        region->offset_uv += width * x;
+        region->width = width;
+        region->height = height;
+    }
+    else // (out->layout == STITCH_LAYOUT_Vertical)
+    {
+        int height = (out->height / in_count + 1) & ~1;
+        region->offset_y = out->stride * (height * index);
+        region->offset_uv += out->stride * (height * index / 2);
+        region->width = out->width;
+        region->height = height;
+    }
+}
+
+static int stitchOneFrame(StitcherContext *ctx)
+{
+    StitcherPort *in = NULL;
+    StitcherPort *out = &ctx->out;
+    int in_count = 0;
+    do
+    {
+        pthread_mutex_lock(out->count_mutex);
+        in_count = ctx->in_count;
+        pthread_mutex_unlock(out->count_mutex);
+        if (in_count > 0)
+            break;
+        else
+            sleep(1);
+    } while (1);
+
+    for (int i = 0; i < in_count; i++)
+    {
+        in = &ctx->in[i];
+        if (in->index == 0)
+        {
+            sem_wait(&ctx->sem_ready); // wait for picture from input port0.
+            if (ctx->exitcode == 1)
+                return 1;
+        }
+        StitcherRegion region;
+        getRegion(out, in->index, in_count, &region);
+        pthread_mutex_lock(&in->pic_mutex);
+        int width = STITCHER_MIN(region.width, in->width);
+        int height = STITCHER_MIN(region.height, in->height);
+        void *dst = out->buffer + out->offset + region.offset_y;
+        void *src = in->buffer + in->offset;
+        //fprintf(stderr, "%d: %p, %d, %d, %p, %d\n", in->index, out->buffer, out->offset, region.offset_y, in->buffer, in->offset);
+        //fprintf(stderr, "%d: %dx%d, %d, %d\n", in->index, width, height, in->stride, out->stride);
+        if (in->available_bufs > 0)
+        {
+            if (in->format == PLINK_COLOR_FormatYUV420SemiPlanarP010 ||
+                in->format == PLINK_COLOR_FormatRawBayer10bit ||
+                in->format == PLINK_COLOR_FormatRawBayer12bit)
+            {
+                int shift = in->format == PLINK_COLOR_FormatYUV420SemiPlanarP010 ? 2 : 4;
+                unsigned int temp[4];
+                for (int h = 0; h < height; h++)
+                {
+                    unsigned int *dst32 = (unsigned int *)dst;
+                    unsigned short *src16 = (unsigned short *)src;
+                    for (int w = 0; w < width; w+=4)
+                    {
+                        temp[0] = ((unsigned int)(src16[w+0] >> shift) & 0xFF);
+                        temp[1] = ((unsigned int)(src16[w+1] >> shift) & 0xFF) << 8;
+                        temp[2] = ((unsigned int)(src16[w+2] >> shift) & 0xFF) << 16;
+                        temp[3] = ((unsigned int)(src16[w+3] >> shift) & 0xFF) << 24;
+                        dst32[w>>2] = temp[0] | temp[1] | temp[2] | temp[3];
+                    }
+                    dst += out->stride;
+                    src += in->stride;
+                }
+            }
+            else
+            {
+                for (int h = 0; h < height; h++)
+                {
+                    memcpy(dst, src, width);
+                    dst += out->stride;
+                    src += in->stride;
+                }
+            }
+        }
+        else
+        {
+            for (int h = 0; h < height; h++)
+            {
+                memset(dst, 0x00, width);
+                dst += out->stride;
+            }
+        }
+        dst = out->buffer + out->offset_uv + region.offset_uv;
+        src = in->buffer + in->offset_uv;
+        //fprintf(stderr, "%d: %p, %d, %d, %p, %d\n", in->index, out->buffer, out->offset_uv, region.offset_uv, in->buffer, in->offset_uv);
+        if (in->format == PLINK_COLOR_FormatYUV420SemiPlanar &&
+            in->available_bufs > 0)
+        {
+            for (int h = 0; h < height/2; h++)
+            {
+                memcpy(dst, src, width);
+                dst += out->stride;
+                src += in->stride;
+            }
+        }
+        else // treat all the other formats as monochrome
+        {
+            for (int h = 0; h < height/2; h++)
+            {
+                memset(dst, 0x80, width);
+                dst += out->stride;
+            }
+        }
+        pthread_mutex_unlock(&in->pic_mutex);
+        if (in->index == 0)
+            sem_post(&ctx->sem_done); // Resume input port0.
+        // !Assume output thread is fast enough, so input port0 won't be blocked for long time.
+    }
+
+    return 0;
+}
+
+static void constructYuvInfo(PlinkYuvInfo *info, StitcherParams *params, unsigned int bus_address, int id)
+{
+    int size_y = params->width * params->stride;
+    int size_uv = size_y / 2;
+    int size_yuv = size_y + size_uv;
+    
+    info->header.type = PLINK_TYPE_2D_YUV;
+    info->header.size = DATA_SIZE(*info);
+    info->header.id = id + 1;
+
+    info->format = params->format;
+    info->bus_address_y = bus_address;
+    info->bus_address_u = info->bus_address_y + size_y;
+    info->bus_address_v = info->bus_address_u + (size_uv / 2);
+    info->pic_width = params->width;
+    info->pic_height = params->height;
+    info->stride_y = params->stride;
+    info->stride_u = 
+        params->format == PLINK_COLOR_FormatYUV420Planar ? 
+        params->stride/2 : params->stride;
+    info->stride_v = info->stride_u;
+}
+
+int getBufferCount(PlinkPacket *pkt)
+{
+    int ret = 0;
+    for (int i = 0; i < pkt->num; i++)
+    {
+        PlinkDescHdr *hdr = (PlinkDescHdr *)(pkt->list[i]);
+        if (hdr->type == PLINK_TYPE_MESSAGE)
+        {
+            int *data = (int *)(pkt->list[i] + DATA_HEADER_SIZE);
+            if (*data == PLINK_EXIT_CODE)
+            {
+                ret |= 0x80000000; // set bit 31 to 1 to indicate 'exit'
+            }
+            else if (*data >= 0)
+                ret++;
+        }
+    }
+
+    return ret;
+}
+
+static void retreiveSentBuffers(PlinkHandle plink, StitcherPort *port)
+{
+    PlinkStatus sts = PLINK_STATUS_OK;
+    PlinkPacket pkt = {0};
+    while (port->available_bufs < NUM_OF_BUFFERS && sts == PLINK_STATUS_OK)
+    {
+        do
+        {
+            sts = PLINK_recv(plink, port->id, &pkt);
+            int count = getBufferCount(&pkt);
+            if (count > 0)
+            {
+                port->available_bufs += count;
+            }
+        } while (sts == PLINK_STATUS_MORE_DATA);
+    }
+}
+
+static void *input_thread(void *args)
+{
+    StitcherPort *port = (StitcherPort *)args;
+    PlinkHandle plink = NULL;
+    PlinkStatus sts = PLINK_STATUS_OK;
+    void *vmem = port->vmem;
+
+    pthread_mutex_init(&port->pic_mutex, NULL);
+
+    if (PLINK_create(&plink, port->name, PLINK_MODE_CLIENT) != PLINK_STATUS_OK)
+        return NULL;
+
+    do
+    {
+        sts = PLINK_connect_ex(plink, NULL, 1000);
+    } while (sts == PLINK_STATUS_TIMEOUT && *port->exit == 0);
+    if (sts != PLINK_STATUS_OK)
+    {
+        PLINK_close(plink, 0);
+        return NULL;
+    }
+
+    pthread_mutex_lock(port->count_mutex);
+    port->index = *(port->in_count);
+    *(port->in_count) = *(port->in_count) + 1;
+    pthread_mutex_unlock(port->count_mutex);
+
+    PlinkPacket sendpkt = {0};
+    PlinkPacket recvpkt = {0};
+    PlinkMsg msg = {0};
+    VmemParams params;
+    int exitcode = 0;
+    do {
+        sts = PLINK_recv(plink, 0, &recvpkt);
+        if (sts == PLINK_STATUS_ERROR)
+            break;
+
+        for (int i = 0; i < recvpkt.num; i++)
+        {
+            PlinkDescHdr *hdr = (PlinkDescHdr *)(recvpkt.list[i]);
+            if (hdr->type == PLINK_TYPE_2D_YUV || 
+                hdr->type == PLINK_TYPE_2D_RAW)
+            {
+                // return previous buffer to source
+                if (sendpkt.num > 0)
+                {
+                    if (port->index == 0)
+                        sem_wait(port->sem_done);
+                    pthread_mutex_lock(&port->pic_mutex);
+                    if (VMEM_release(vmem, &params) != VMEM_STATUS_OK)
+                        fprintf(stderr, "[STITCHER] ERROR: Failed to release buffer.\n");
+                    sts = PLINK_send(plink, 0, &sendpkt);
+                    if (sts == PLINK_STATUS_ERROR)
+                        break;
+                    if (sendpkt.fd != PLINK_INVALID_FD)
+                        close(sendpkt.fd);
+                    port->available_bufs--;
+                    sendpkt.num = 0;
+                }
+                else
+                    pthread_mutex_lock(&port->pic_mutex);
+
+                memset(&params, 0, sizeof(params));
+                if (recvpkt.fd != PLINK_INVALID_FD)
+                {
+                    params.fd = recvpkt.fd;
+                    if (VMEM_import(vmem, &params) != VMEM_STATUS_OK)
+                        break;
+                    if (VMEM_mmap(vmem, &params) != VMEM_STATUS_OK)
+                        break;
+                }
+
+                if (port->index == 0)
+                    sem_post(port->sem_ready); // signal output thread that one picture is ready
+            }
+
+            if (hdr->type == PLINK_TYPE_2D_YUV)
+            {
+                PlinkYuvInfo *pic = (PlinkYuvInfo *)(recvpkt.list[i]);
+                printf("[STITCHER] Input%d: Received YUV frame %d 0x%010llx from %s: fd %d, %dx%d, stride = luma %d, chroma %d\n", 
+                        port->index, pic->header.id, pic->bus_address_y, port->name, recvpkt.fd,
+                        pic->pic_width, pic->pic_height,
+                        pic->stride_y, pic->stride_u);
+
+                port->buffer = params.vir_address;
+                port->format = pic->format;
+                port->width = pic->pic_width;
+                port->height = pic->pic_height;
+                port->stride = pic->stride_y;
+                port->offset = pic->offset_y;
+                port->offset_uv = pic->offset_u > 0 ? pic->offset_u : (pic->offset_y + pic->pic_height*pic->stride_y);
+            }
+            else if (hdr->type == PLINK_TYPE_2D_RAW)
+            {
+                PlinkRawInfo *img = (PlinkRawInfo *)(recvpkt.list[i]);
+                printf("[STITCHER] Input%d: Received RAW frame %d 0x%010llx from %s: fd %d, %dx%d, stride %d\n", 
+                        port->index, img->header.id, img->bus_address, port->name, recvpkt.fd,
+                        img->img_width, img->img_height, img->stride);
+
+                port->buffer = params.vir_address;
+                port->format = img->format;
+                port->width = img->img_width;
+                port->height = img->img_height;
+                port->stride = img->stride;
+                port->offset = img->offset;
+            }
+            else if (hdr->type == PLINK_TYPE_MESSAGE)
+            {
+                PlinkMsg *msg = (PlinkMsg *)(recvpkt.list[i]);
+                if (msg->msg == PLINK_EXIT_CODE)
+                {
+                    exitcode = 1;
+                    printf("[STITCHER] Input %d: Exit\n", port->index);
+                    break;
+                }
+            }
+
+            if (hdr->type == PLINK_TYPE_2D_YUV ||
+                hdr->type == PLINK_TYPE_2D_RAW)
+            {
+                port->available_bufs++;
+                pthread_mutex_unlock(&port->pic_mutex);
+
+                msg.header.type = PLINK_TYPE_MESSAGE;
+                msg.header.size = DATA_SIZE(PlinkMsg);
+                msg.msg = hdr->id;
+                sendpkt.list[0] = &msg;
+                sendpkt.num = 1;
+                sendpkt.fd = recvpkt.fd;
+            }
+        }
+    } while (exitcode == 0 && *port->exit == 0 && sts != PLINK_STATUS_ERROR);
+
+    if (sendpkt.num > 0)
+    {
+        pthread_mutex_lock(&port->pic_mutex);
+        if (VMEM_release(vmem, &params) != VMEM_STATUS_OK)
+            fprintf(stderr, "[STITCHER] ERROR: Failed to release buffer.\n");
+        sts = PLINK_send(plink, 0, &sendpkt);
+        port->available_bufs--;
+        pthread_mutex_unlock(&port->pic_mutex);
+    }
+
+    if (port->index == 0)
+    {
+        *port->exit = 1;
+        sem_post(port->sem_ready);
+    }
+    msg.header.type = PLINK_TYPE_MESSAGE;
+    msg.header.size = DATA_SIZE(PlinkMsg);
+    msg.msg = PLINK_EXIT_CODE;
+    sendpkt.list[0] = &msg;
+    sendpkt.num = 1;
+    sendpkt.fd = PLINK_INVALID_FD;
+    sts = PLINK_send(plink, 0, &sendpkt);
+    PLINK_close(plink, 0);
+    pthread_mutex_destroy(&port->pic_mutex);
+    return NULL;
+}
+
+int main(int argc, char **argv) {
+    PlinkStatus sts = PLINK_STATUS_OK;
+    StitcherParams params;
+    StitcherContext ctx;
+    PlinkHandle plink = NULL;
+    PlinkPacket pkt = {0};
+    PlinkYuvInfo pic;
+    PlinkMsg msg;
+
+    parseParams(argc, argv, &params);
+    if (checkParams(&params) != 0)
+    {
+        printUsage(argv[0]);
+        return 0;
+    }
+
+    memset(&ctx, 0, sizeof(ctx));
+
+    void *vmem = NULL;
+    if (VMEM_create(&vmem) != VMEM_STATUS_OK)
+        errExit("Failed to create VMEM.");
+
+    int width = params.width;
+    int height = params.height;
+    int stride = params.stride;
+    StitchLayout layout = params.layout;
+    int size = getBufferSize(&params);
+    if (size == 0)
+        errExit("Wrong format or wrong resolution.");
+    PictureBuffer picbuffers[NUM_OF_BUFFERS];
+    AllocateBuffers(picbuffers, size, vmem);
+
+    sem_init(&ctx.sem_ready, 0, 0);
+    sem_init(&ctx.sem_done, 0, 0);
+    pthread_mutex_init(&ctx.count_mutex, NULL);
+
+    pthread_t thread_in[MAX_NUM_OF_INPUTS];
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    for (int i = 0; i < MAX_NUM_OF_INPUTS; i++)
+    {
+        ctx.in[i].name = params.in_name[i];
+        ctx.in[i].count_mutex = &ctx.count_mutex;
+        ctx.in[i].sem_ready = &ctx.sem_ready;
+        ctx.in[i].sem_done = &ctx.sem_done;
+        ctx.in[i].in_count = &ctx.in_count;
+        ctx.in[i].exit = &ctx.exitcode;
+        ctx.in[i].vmem = vmem;
+        if (pthread_create(&thread_in[i], &attr, input_thread, &ctx.in[i]) != 0)
+            fprintf(stderr, "[STITCHER] ERROR: Failed to create thread for input %d\n", i);
+    }
+
+    sts = PLINK_create(&plink, params.out_name, PLINK_MODE_SERVER);
+
+    StitcherPort *out = &ctx.out;
+    out->available_bufs = NUM_OF_BUFFERS;
+    out->count_mutex = &ctx.count_mutex;
+    out->sem_ready = &ctx.sem_ready;
+    out->sem_done = &ctx.sem_done;
+    out->format = params.format;
+    out->layout = params.layout;
+    out->width = params.width;
+    out->height = params.height;
+    out->stride = params.stride;
+    out->offset = 0;
+    out->offset_uv = params.height * params.stride;
+    out->exit = &ctx.exitcode;
+    sts = PLINK_connect(plink, &out->id);
+
+    int exitcode = 0;
+    do {
+        int sendid = out->sendid;
+        out->buffer = picbuffers[sendid].virtual_address;
+        if (stitchOneFrame(&ctx) != 0)
+            break;
+        constructYuvInfo(&pic, &params, picbuffers[sendid].bus_address, sendid);
+        printf("[STITCHER] Processed frame %d 0x%010llx: %dx%d, stride = luma %d, chroma %d\n", 
+                sendid, pic.bus_address_y, 
+                pic.pic_width, pic.pic_height,
+                pic.stride_y, pic.stride_u);
+
+        pkt.list[0] = &pic;
+        pkt.num = 1;
+        pkt.fd = picbuffers[sendid].fd;
+        sts = PLINK_send(plink, out->id, &pkt);
+        out->sendid = (out->sendid + 1) % NUM_OF_BUFFERS;
+        out->available_bufs -= 1;
+
+        int timeout = out->available_bufs == 0 ? 100 : 0;
+        if (PLINK_wait(plink, out->id, timeout) == PLINK_STATUS_OK)
+        {
+            do
+            {
+                sts = PLINK_recv(plink, out->id, &pkt);
+                int count = getBufferCount(&pkt);
+                if (count < 0)
+                    exitcode = 1;
+                out->available_bufs += count;
+            } while (sts == PLINK_STATUS_MORE_DATA);
+        }
+    } while (exitcode == 0);
+
+cleanup:
+    ctx.exitcode = 1;
+    sem_post(&ctx.sem_done);
+    msg.header.type = PLINK_TYPE_MESSAGE;
+    msg.header.size = DATA_SIZE(PlinkMsg);
+    msg.msg = PLINK_EXIT_CODE;
+    pkt.list[0] = &msg;
+    pkt.num = 1;
+    pkt.fd = PLINK_INVALID_FD;
+    sts = PLINK_send(plink, out->id, &pkt);
+    retreiveSentBuffers(plink, out);
+    PLINK_recv_ex(plink, out->id, &pkt, 1000);
+    for (int i = 0; i < MAX_NUM_OF_INPUTS; i++)
+        pthread_join(thread_in[i], NULL);
+    //sleep(1); // Sleep one second to make sure client is ready for exit
+    if (vmem)
+        FreeBuffers(picbuffers, vmem);
+    PLINK_close(plink, PLINK_CLOSE_ALL);
+    VMEM_destroy(vmem);
+    pthread_mutex_destroy(&ctx.count_mutex);
+    sem_destroy(&ctx.sem_ready);
+    sem_destroy(&ctx.sem_done);
+    exit(EXIT_SUCCESS);
+}
+

+ 0 - 75
user_mode/Makefile

@@ -1,75 +0,0 @@
-
-CFLAGS = -Wall -D_GNU_SOURCE -D_REENTRANT -D_THREAD_SAFE -O2 -Werror -Wno-unused -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-strict-overflow -Wno-array-bounds -Wno-shift-negative-value -Wempty-body -Wtype-limits -Wno-unused-result -fPIC -Wmissing-field-initializers -std=gnu99
-
-INCLUDE += -I../kernel_mode
-
-SRCS = isp_venc_shake_hal.c
-OBJS = $(SRCS:.c=.o)
-
-#source search path
-vpath %.c
-
-# name of the outputfile (library)
-IVSLIB = libivs.a
-IVSSLIB = libivs.so
-
-#Here are rules for building codes and generating object library.
-all:    tags
-	@echo ---------------------------------------
-	@echo "Usage: make [ system | testdata | versatile | integrator | android]"
-	@echo "system     - PC system model (== pclinux)"
-	@echo "testdata   - PC system model for test data creation"
-	@echo "eval       - PC system model for evaluation with frame limit"
-	@echo "versatile  - ARM versatile with FPGA HW"
-	@echo "integrator - ARM integrator with FPGA HW"
-	@echo "android    - ARM android"
-	@echo "NOTE! Make sure to do 'make clean'"
-	@echo "between compiling to different targets!"
-	@echo ---------------------------------------
-
-.PHONY: system_lib system testdata clean depend
-
-evaluation: eval
-eval: system
-
-system_static: testdata
-system: testdata
-
-# for libva
-system_lib: system
-
-testdata: .depend $(IVSLIB)
-
-
-.PHONY: hwlinux
-hwlinux:
-hwlinux: .depend $(IVSLIB) $(IVSSLIB)
-
-$(IVSLIB): $(OBJS)
-	@echo "[AR] $@"
-	$(AR) rcs $(IVSLIB) $(OBJS)
-
-$(IVSSLIB): $(OBJS)
-	$(CC) -fPIC -Wl,-no-undefined -shared $(CFLAGS) $(OBJS) -lm -lpthread -o $(IVSSLIB)
-
-
-%.o: %.c
-	$(CC) -c $(CFLAGS) $(INCLUDE) $< -o $@
-
-clean:
-	$(RM) $(IVSLIB) $(IVSSLIB)
-	$(RM) .depend
-	$(RM) *.o *.gcno *.gcda
-
-tags:
-	ctags ../kernel_mode/*h ./*[ch]
-
-depend .depend: $(SRCS)
-	@echo "[CC] $@"
-	@$(CC) -M $(DEBFLAGS) $(INCLUDE) $^ > .depend
-
-ifneq (clean, $(findstring clean, $(MAKECMDGOALS)))
-ifeq (.depend, $(wildcard .depend))
-include .depend
-endif
-endif

+ 0 - 160
user_mode/isp_venc_shake_hal.c

@@ -1,160 +0,0 @@
-/*
- * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
- * License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <malloc.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include "isp_venc_shake_driver.h"
-#include "isp_venc_shake_hal.h"
-#include <sys/ioctl.h>
-
-#ifndef NULL
-#define NULL    ((void *)0)
-#endif
-
-
-#ifndef IVS_MODULE_PATH
-#define IVS_MODULE_PATH             "/dev/shake"
-#endif
-
-typedef struct _IspVencShake
-{
-    unsigned int fd;
-    struct ivs_parameter params;
-} IspVencShake;
-
-IvsStatus createIspVencShake(void **ivs)
-{
-    IvsStatus status = IVS_STATUS_OK;
-    IspVencShake *shake = NULL;
-    unsigned int fd;
-    
-    shake = (IspVencShake *)malloc(sizeof(IspVencShake));
-    if (NULL == shake)
-        return IVS_STATUS_ERROR;
-
-    fd = open("/dev/shake", O_RDONLY);
-    if (fd == -1)
-    {
-        free(shake);
-        return IVS_STATUS_ERROR;
-    }
-
-    shake->fd = fd;
-    *ivs = shake;
-
-    return status;
-}
-
-IvsStatus configIspVencShake(void *ivs, IvsConfig *config)
-{
-    IvsStatus status = IVS_STATUS_OK;
-    IspVencShake *shake = (IspVencShake *)ivs;
-    struct ivs_parameter *params = NULL;
-    unsigned int buffer_height;
-
-    if (ivs == NULL || config == NULL)
-        return IVS_STATUS_ERROR;
-
-    params = &shake->params;
-    buffer_height = config->mode = IVS_BUFFER_MODE_SLICE ? 
-        config->buffer_height : config->pic_height * 3 / 2;
-
-    params->pic_width = config->pic_width;
-    params->pic_height = config->pic_height;
-    params->encode_width = config->encode_width;
-    params->encode_height = config->encode_height;
-    params->wid_y = 1;
-    params->wid_uv = 2;
-    params->sram_size = config->pic_width * buffer_height;
-    params->encode_n = config->encode == IVS_ENCODER_FORMAT_H264 ? 16 : 64;
-    params->stride_y = config->stride_y;
-    params->stride_uv = config->stride_uv;
-    params->encode_x = config->encode_x;
-    params->encode_y = config->encode_y;
-    params->int_mask = 0;
-
-    if (ioctl(shake->fd, THEAD_IOCH_CONFIG_IVS, params) == -1)
-    {
-        status = IVS_STATUS_ERROR;
-    }
-
-    return status;
-}
-
-IvsStatus startIspVencShake(void *ivs)
-{
-    IvsStatus status = IVS_STATUS_OK;
-    IspVencShake *shake = (IspVencShake *)ivs;
-
-    if (ivs == NULL)
-        return IVS_STATUS_ERROR;
-
-    if (ioctl(shake->fd, THEAD_IOCH_START_IVS, NULL) == -1)
-    {
-        status = IVS_STATUS_ERROR;
-    }
-
-    return status;
-}
-
-IvsStatus resetIspVencShake(void *ivs)
-{
-    IvsStatus status = IVS_STATUS_OK;
-    IspVencShake *shake = (IspVencShake *)ivs;
-
-    if (ivs == NULL)
-        return IVS_STATUS_ERROR;
-
-    if (ioctl(shake->fd, THEAD_IOCH_RESET_IVS, NULL) == -1)
-    {
-        status = IVS_STATUS_ERROR;
-    }
-
-    return status;
-}
-
-IvsStatus getIspVencShakeState(void *ivs, int *state)
-{
-    IvsStatus status = IVS_STATUS_OK;
-    IspVencShake *shake = (IspVencShake *)ivs;
-
-    if (ivs == NULL || state == NULL)
-        return IVS_STATUS_ERROR;
-
-    if (ioctl(shake->fd, THEAD_IOCH_GET_STATE, state) == -1)
-    {
-        status = IVS_STATUS_ERROR;
-    }
-
-    return status;
-}
-
-void destroyIspVencShake(void *ivs)
-{
-    IspVencShake *shake = (IspVencShake *)ivs;
-
-    if (NULL == ivs)
-        return;
-
-    if (shake->fd != -1)
-        close(shake->fd);
-
-    free(ivs);
-}
-

+ 0 - 75
user_mode/isp_venc_shake_hal.h

@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2021-2022 Alibaba Group. All rights reserved.
- * License-Identifier: Apache-2.0
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _ISP_VENC_SHAKE_HAL_H_
-#define _ISP_VENC_SHAKE_HAL_H_
-
-#ifndef IVS_MODULE_PATH
-#define IVS_MODULE_PATH "/tmp/dev/theadivs"
-#endif
-
-typedef enum _IvsStatus
-{
-    IVS_STATUS_OK = 0,
-    IVS_STATUS_ERROR = -1,
-} IvsStatus;
-
-typedef enum _IvsState
-{
-    IVS_STATE_IDLE,
-    IVS_STATE_READY,
-    IVS_STATE_RUNNING,
-    IVS_STATE_ERROR
-} IvsState;
-
-typedef enum _IvsBufferMode
-{
-    IVS_BUFFER_MODE_FRAME = 0,
-    IVS_BUFFER_MODE_SLICE,
-    IVS_BUFFER_MODE_MAX
-} IvsBufferMode;
-
-typedef enum _IvsEncoderFormat
-{
-    IVS_ENCODER_FORMAT_H264 = 0,
-    IVS_ENCODER_FORMAT_H265,
-    IVS_ENCODER_FORMAT_MAX
-} IvsEncoderFormat;
-
-typedef struct _IvsConfig
-{
-    IvsBufferMode mode;
-    IvsEncoderFormat encode;
-    unsigned int pic_width;
-    unsigned int pic_height;
-    unsigned int encode_width;
-    unsigned int encode_height;
-    unsigned int buffer_height;   // valid in slice buffer mode only
-    unsigned int stride_y;
-    unsigned int stride_uv;
-    unsigned int encode_x;
-    unsigned int encode_y;
-} IvsConfig;
-
-IvsStatus createIspVencShake(void **ivs);
-IvsStatus configIspVencShake(void *ivs, IvsConfig *config);
-IvsStatus startIspVencShake(void *ivs);
-IvsStatus resetIspVencShake(void *ivs);
-IvsStatus getIspVencShakeState(void *ivs, int *state);
-void destroyIspVencShake(void *ivs);
-
-#endif /* !_ISP_VENC_SHAKE_HAL_H_ */

Some files were not shown because too many files changed in this diff