Linux_SDK_V1.0.3

This commit is contained in:
thead_admin
2023-01-05 17:50:07 +08:00
parent 3d0966dbf7
commit ee655750f9
22 changed files with 3603 additions and 1118 deletions

27
.gitignore vendored
View File

@@ -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/

139
Makefile
View File

@@ -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: lib server client stitcher
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
lib:
$(CC) $(LIBSRCS) $(CFLAGS) -shared -o $(LIBNAME)
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)
server: lib
$(CC) $(server_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(server_NAME)
driver:
@echo $(BUILD_LOG_START)
make -C $(LINUX_DIR) M=$(PWD)/kernel_mode ARCH=$(ARCH) modules
@echo $(BUILD_LOG_END)
client: lib
$(CC) $(client_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(client_NAME)
clean_driver:
@echo $(BUILD_LOG_START)
make -C kernel_mode KDIR=$(LINUX_DIR) clean
@echo $(BUILD_LOG_END)
stitcher: lib
$(CC) $(stitcher_SRCS) $(CFLAGS) -L$(OUTPUTDIR) -L$(LIB_PATH)/vidmem -lplink -lvmem -ldl -pthread -o $(stitcher_NAME)
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)
install_addons: install_prepare
@echo $(BUILD_LOG_START)
@echo $(BUILD_LOG_END)
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)
install_rootfs: install_local_output
@echo $(BUILD_LOG_START)
@echo $(BUILD_LOG_END)
clean_output:
@echo $(BUILD_LOG_START)
rm -rf ./output
@echo $(BUILD_LOG_END)
clean: clean_output clean_driver clean_lib clean_test
clean:
rm -rf $(OUTPUTDIR)
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@

View File

@@ -1,12 +1,57 @@
# How to build
- Just run 'make' to build kernel mode driver, user mode driver and test application.
# 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.
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
- **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.
# Description of each directories
- kernel_mode: kernel mode driver source
- user_mode: user mode driver source
- test: test application source
## 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 Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
doc/pics/packet.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

1141
doc/plink-api.md Normal file

File diff suppressed because it is too large Load Diff

225
inc/process_linker.h Normal file
View File

@@ -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
inc/process_linker_types.h Normal file
View File

@@ -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_ */

View File

@@ -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

View File

@@ -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");

View File

@@ -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
src/process_linker.c Normal file
View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
test/plink_client.c Normal file
View File

@@ -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
test/plink_server.c Normal file
View File

@@ -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
test/plink_stitcher.c Normal file
View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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_ */