mirror of
https://github.com/thead-yocto-mirror/process-linker
synced 2026-06-21 09:02:31 +02:00
Linux_SDK_V1.0.3
This commit is contained in:
27
.gitignore
vendored
27
.gitignore
vendored
@@ -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
139
Makefile
@@ -1,120 +1,47 @@
|
||||
##
|
||||
# Copyright (C) 2020 Alibaba Group Holding Limited
|
||||
##
|
||||
ifneq ($(wildcard ../.param),)
|
||||
include ../.param
|
||||
include ../.param
|
||||
endif
|
||||
|
||||
#CONFIG_DEBUG_MODE=1
|
||||
CONFIG_OUT_ENV=hwlinux
|
||||
INC_PATH ?= /usr/include
|
||||
LIB_PATH ?= /usr/lib
|
||||
|
||||
CONFIG_BUILD_DRV_EXTRA_PARAM:=""
|
||||
CONFIG_BUILD_LIB_EXTRA_PARAM:=""
|
||||
CONFIG_BUILD_TST_EXTRA_PARAM:=""
|
||||
OUTPUTDIR = ./output
|
||||
LIBNAME = $(OUTPUTDIR)/libplink.so
|
||||
server_NAME = $(OUTPUTDIR)/plinkserver
|
||||
client_NAME = $(OUTPUTDIR)/plinkclient
|
||||
stitcher_NAME = $(OUTPUTDIR)/plinkstitcher
|
||||
|
||||
DIR_TARGET_BASE=bsp/ivs
|
||||
DIR_TARGET_LIB =bsp/ivs/lib
|
||||
DIR_TARGET_KO =bsp/ivs/ko
|
||||
DIR_TARGET_TEST=bsp/ivs/test
|
||||
INCS = ./inc
|
||||
LIBSRCS = ./src/process_linker.c
|
||||
LIBOBJS = $(LIBSRCS:.c=.o)
|
||||
server_SRCS = ./test/plink_server.c
|
||||
server_OBJS = $(server_SRCS:.c=.o)
|
||||
client_SRCS = ./test/plink_client.c
|
||||
client_OBJS = $(client_SRCS:.c=.o)
|
||||
stitcher_SRCS = ./test/plink_stitcher.c
|
||||
stitcher_OBJS = $(stitcher_SRCS:.c=.o)
|
||||
|
||||
MODULE_NAME=IVS
|
||||
BUILD_LOG_START="\033[47;30m>>> $(MODULE_NAME) $@ begin\033[0m"
|
||||
BUILD_LOG_END ="\033[47;30m<<< $(MODULE_NAME) $@ end\033[0m"
|
||||
CFLAGS = -I$(INCS) -I$(INC_PATH)/vidmem
|
||||
CFLAGS += -pthread -fPIC -O
|
||||
|
||||
#
|
||||
# Do a parallel build with multiple jobs, based on the number of CPUs online
|
||||
# in this system: 'make -j8' on a 8-CPU system, etc.
|
||||
#
|
||||
# (To override it, run 'make JOBS=1' and similar.)
|
||||
#
|
||||
$(shell if [ ! -e $(OUTPUTDIR) ];then mkdir -p $(OUTPUTDIR); fi)
|
||||
|
||||
ifeq ($(JOBS),)
|
||||
JOBS := $(shell grep -c ^processor /proc/cpuinfo 2>/dev/null)
|
||||
ifeq ($(JOBS),)
|
||||
JOBS := 1
|
||||
endif
|
||||
endif
|
||||
all: 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 $@
|
||||
|
||||
65
README.md
65
README.md
@@ -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
BIN
doc/pics/comm.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
doc/pics/packet.PNG
Normal file
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
BIN
doc/pics/plink-sw-stack.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
doc/pics/thead-logo-eng-white-wide.png
Normal file
BIN
doc/pics/thead-logo-eng-white-wide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
1141
doc/plink-api.md
Normal file
1141
doc/plink-api.md
Normal file
File diff suppressed because it is too large
Load Diff
225
inc/process_linker.h
Normal file
225
inc/process_linker.h
Normal 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
196
inc/process_linker_types.h
Normal 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_ */
|
||||
@@ -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
|
||||
@@ -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(¶ms, (struct ivs_parameter*)arg, sizeof(struct ivs_parameter));
|
||||
config_ivs(filp, ¶ms);
|
||||
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");
|
||||
|
||||
@@ -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
552
src/process_linker.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
203
test/plink_client.c
Normal 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(¶ms, 0, sizeof(params));
|
||||
if (recvpkt.fd != PLINK_INVALID_FD)
|
||||
{
|
||||
params.fd = recvpkt.fd;
|
||||
if (VMEM_import(vmem, ¶ms) != VMEM_STATUS_OK)
|
||||
errExit("Failed to import fd.");
|
||||
if (VMEM_mmap(vmem, ¶ms) != 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, ¶ms) != 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
432
test/plink_server.c
Normal 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, ¶ms);
|
||||
VMEM_mmap(vmem, ¶ms);
|
||||
VMEM_export(vmem, ¶ms);
|
||||
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(¶ms, 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, ¶ms);
|
||||
}
|
||||
}
|
||||
|
||||
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, ¶ms);
|
||||
if (checkParams(¶ms) != 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(¶ms);
|
||||
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, ¶ms, 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, ¶ms, 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
766
test/plink_stitcher.c
Normal 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, ¶ms);
|
||||
VMEM_mmap(vmem, ¶ms);
|
||||
VMEM_export(vmem, ¶ms);
|
||||
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(¶ms, 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, ¶ms);
|
||||
}
|
||||
}
|
||||
|
||||
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, ®ion);
|
||||
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, ¶ms) != 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(¶ms, 0, sizeof(params));
|
||||
if (recvpkt.fd != PLINK_INVALID_FD)
|
||||
{
|
||||
params.fd = recvpkt.fd;
|
||||
if (VMEM_import(vmem, ¶ms) != VMEM_STATUS_OK)
|
||||
break;
|
||||
if (VMEM_mmap(vmem, ¶ms) != 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, ¶ms) != 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, ¶ms);
|
||||
if (checkParams(¶ms) != 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(¶ms);
|
||||
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, ¶ms, 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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user