931 lines
30 KiB
C++
931 lines
30 KiB
C++
/*
|
|
* Copyright (C) 2024 Amlogic Corporation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#define FUSE_USE_VERSION 31
|
|
|
|
#include <fuse3/fuse.h>
|
|
#include <fuse3/cuse_lowlevel.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <linux/kdev_t.h>
|
|
#include <linux/videodev2.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
|
|
#include <string>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unordered_map>
|
|
|
|
#include "vsc_fuse.h"
|
|
#include "v4l2-controls-ext_func.h"
|
|
|
|
#include "BinderClient.h"
|
|
#include <poll.h>
|
|
|
|
#define MSTONS (1000 * 1000)
|
|
#define STONS (1000 * MSTONS)
|
|
|
|
#define DEV_VIDEO_100 100
|
|
#define DEV_VIDEO_101 101
|
|
#define DEV_VIDEO_32 "DEVNAME=video102"
|
|
|
|
static int subscribe_item[] = {
|
|
V4L2_CID_EXT_VSC_SUBSCRIBE_ACTIVE_WIN,
|
|
V4L2_CID_EXT_VSC_SUBSCRIBE_APPLYING_DONE_WINDOW_REGION,
|
|
V4L2_CID_EXT_VSC_SUBSCRIBE_BSP_ERROR,
|
|
V4L2_CID_EXT_VSC_SUBSCRIBE_MUTE_OFF,
|
|
V4L2_CID_EXT_VSC_SUBSCRIBE_VIDEO_DELAY,
|
|
};
|
|
|
|
static int get_file_index(video_scaler_info_t *vs)
|
|
{
|
|
int index = 0;
|
|
|
|
while (index >= 0) {
|
|
if (vs->Indexs.count(index) > 0)
|
|
index++;
|
|
else {
|
|
vs->Indexs.emplace(index);
|
|
break;
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
static void scaler_open(fuse_req_t req, struct fuse_file_info *fi)
|
|
{
|
|
static video_scaler_info_t *vs = (video_scaler_info_t *)fuse_req_userdata(req);
|
|
video_scaler_file_t *file = NULL;
|
|
|
|
if (!vs) {
|
|
fuse_reply_err(req, ENOMEM);
|
|
return;
|
|
}
|
|
|
|
file = (video_scaler_file_t *)calloc(1, sizeof(*file));
|
|
if (!file) {
|
|
fuse_reply_err(req, ENOMEM);
|
|
return;
|
|
}
|
|
|
|
file->index = get_file_index(vs);
|
|
file->display_id = vs->display_id;
|
|
file->scaler_id = vs->scaler_id;
|
|
|
|
file->subscribe_size = sizeof(subscribe_item) / sizeof(subscribe_item[0]);
|
|
file->subscribeInfo =
|
|
(struct subscribe_info *)calloc(1, file->subscribe_size * sizeof(*file->subscribeInfo));
|
|
|
|
if (file->subscribeInfo) {
|
|
for (int i = 0; i < file->subscribe_size; i++)
|
|
file->subscribeInfo[i].event_id = subscribe_item[i];
|
|
} else
|
|
file->subscribe_size = 0;
|
|
|
|
pthread_mutex_init(&file->mutex, NULL);
|
|
|
|
file->binder = new BinderClient(file);
|
|
if (file->binder.get())
|
|
file->binder->connectScaler(file->display_id, file->scaler_id, file->index);
|
|
|
|
fi->fh = (uint64_t)file;
|
|
fuse_reply_open(req, fi);
|
|
}
|
|
|
|
static void scaler_release(fuse_req_t req, struct fuse_file_info *fi)
|
|
{
|
|
static video_scaler_info_t *vs = (video_scaler_info_t *)fuse_req_userdata(req);
|
|
video_scaler_file_t *file = (video_scaler_file_t *)fi->fh;
|
|
|
|
if (!vs || !file) {
|
|
fuse_reply_err(req, EINVAL);
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&file->mutex);
|
|
file->pollHandle = NULL;
|
|
file->poll_status = false;
|
|
memset(&file->receiveEvent, 0, sizeof(file->receiveEvent));
|
|
pthread_mutex_unlock(&file->mutex);
|
|
|
|
if (file->binder.get())
|
|
file->binder->disconnectScaler(file->display_id, file->scaler_id, file->index);
|
|
|
|
vs->Indexs.erase(file->index);
|
|
if (file->subscribeInfo)
|
|
free(file->subscribeInfo);
|
|
file->subscribeInfo = NULL;
|
|
|
|
pthread_mutex_destroy(&file->mutex);
|
|
|
|
if (file)
|
|
free(file);
|
|
fi->fh = 0;
|
|
fuse_reply_buf(req, NULL, 0);
|
|
}
|
|
|
|
static void v4l2_read(fuse_req_t req, size_t size, off_t off,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
fuse_reply_buf(req, NULL, 0);
|
|
}
|
|
|
|
static void v4l2_write(fuse_req_t req, const char *buf, size_t size, off_t off,
|
|
struct fuse_file_info *fi)
|
|
{
|
|
fuse_reply_write(req, size);
|
|
}
|
|
|
|
static const std::unordered_map<int, struct ext_ctrls_desc> ext_ctrls = {
|
|
VSC_IOCTL_DEF_DRV(ALPHA_ZORDER, struct v4l2_ext_vsc_zorder_info,
|
|
vsc_ext_ctrls_get_AlphaZorder, vsc_ext_ctrls_set_AlphaZorder),
|
|
VSC_IOCTL_DEF_DRV(ORBIT_POLICY, struct v4l2_ext_vsc_orbit_policy,
|
|
vsc_ext_ctrls_get_OrbitPolicy, vsc_ext_ctrls_set_OrbitPolicy),
|
|
VSC_IOCTL_DEF_DRV(SEAMLESS_MODE, struct v4l2_ext_vsc_seamless_info,
|
|
vsc_ext_ctrls_get_SeamlessInfo, vsc_ext_ctrls_set_SeamlessInfo),
|
|
};
|
|
|
|
static void v4l2_g_ctrls(fuse_req_t req, struct fuse_file_info *fi, const void *in_buf)
|
|
{
|
|
struct v4l2_control *control = (struct v4l2_control *)in_buf;
|
|
int ret = 0;
|
|
|
|
if (!control || !fi) {
|
|
fuse_reply_err(req, EINVAL);
|
|
return;
|
|
}
|
|
|
|
switch (control->id) {
|
|
case V4L2_CID_EXT_VSC_HDR_TYPE:
|
|
ret = v4l2_ext_get_hdr_type(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FREEZE:
|
|
ret = v4l2_ext_get_vsc_freeze(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FRAME_DELAY:
|
|
ret = v4l2_ext_get_vsc_frame_delay(req, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_RGB444:
|
|
ret = v4l2_ext_get_vsc_rgb444(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_OCCUPATION_SUB_SCALER:
|
|
ret = v4l2_ext_get_vsc_occupation_sub_scaler(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FREEZE_FRAME_BUFFER:
|
|
ret = v4l2_ext_get_vsc_freeze_frame_buffer(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ADAPTIVE_STREAM:
|
|
ret = v4l2_ext_get_vsc_adaptive_stream(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_BG_COLOR:
|
|
ret = v4l2_cid_get_bg_color(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_CONNECTION_STATE:
|
|
ret = v4l2_cid_get_hdmitx_connection(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDR_MODE:
|
|
ret = v4l2_cid_get_hdmitx_hdr_mode(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDCP_VERSION:
|
|
ret = v4l2_cid_get_hdmitx_hdcp_version(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDR_POLICY:
|
|
ret = v4l2_cid_get_hdmitx_hdr_policy(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDCP_STATUS:
|
|
ret = v4l2_cid_get_hdmitx_hdcp_status(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_SCAN_MODE:
|
|
ret = v4l2_ext_get_scan_mode(fi, &control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_PIXEL_TO_PIXEL:
|
|
ret = v4l2_ext_get_pixed_to_pixed(fi, &control->value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fuse_reply_ioctl(req, ret, control, sizeof(*control));
|
|
}
|
|
|
|
static void v4l2_s_ctrls(fuse_req_t req, struct fuse_file_info *fi, const void *in_buf)
|
|
{
|
|
struct v4l2_control *control = (struct v4l2_control *)in_buf;
|
|
int ret = 0;
|
|
|
|
if (!control || !fi) {
|
|
fuse_reply_err(req, EINVAL);
|
|
return;
|
|
}
|
|
|
|
switch (control->id) {
|
|
case V4L2_CID_EXT_VSC_HDR_TYPE:
|
|
ret = v4l2_ext_set_hdr_type(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FREEZE:
|
|
ret = v4l2_ext_set_vsc_freeze(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FRAME_DELAY:
|
|
ret = v4l2_ext_set_vsc_frame_delay(req, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_RGB444:
|
|
ret = v4l2_ext_set_vsc_rgb444(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_OCCUPATION_SUB_SCALER:
|
|
ret = v4l2_ext_set_vsc_occupation_sub_scaler(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_FREEZE_FRAME_BUFFER:
|
|
ret = v4l2_ext_set_vsc_freeze_frame_buffer(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ADAPTIVE_STREAM:
|
|
ret = v4l2_ext_set_vsc_adaptive_stream(fi, control->value);
|
|
break;
|
|
case V4L2_CID_BG_COLOR:
|
|
ret = v4l2_cid_set_bg_color(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDR_POLICY:
|
|
ret = v4l2_cid_set_hdmitx_hdr_policy(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_AVMUTE:
|
|
ret = v4l2_cid_set_hdmitx_av_mute(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_HDCP_AUTH:
|
|
ret = v4l2_cid_set_hdmitx_hdcp_auth(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_SCAN_MODE:
|
|
ret = v4l2_ext_set_scan_mode(fi, control->value);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_PIXEL_TO_PIXEL:
|
|
ret = v4l2_ext_set_pixed_to_pixed(fi, control->value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fuse_reply_ioctl_iov(req, ret, NULL, 0);
|
|
}
|
|
|
|
int v4l2_ext_get_edid_length(enum v4l2_ext_hdmitx_edid_size size)
|
|
{
|
|
int len = 0;
|
|
switch (size) {
|
|
case V4L2_EXT_HDMITX_EDID_SIZE_512:
|
|
len = 512;
|
|
break;
|
|
case V4L2_EXT_HDMITX_EDID_SIZE_256:
|
|
len = 256;
|
|
break;
|
|
case V4L2_EXT_HDMITX_EDID_SIZE_128:
|
|
len = 128;
|
|
break;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static void v4l2_g_ext_ctrls(fuse_req_t req, struct fuse_file_info *fi, void *arg,
|
|
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
|
{
|
|
struct iovec iov_r[3], iov_w, iov;
|
|
struct v4l2_ext_controls *ext_controls;
|
|
struct v4l2_ext_control *ext_control;
|
|
int ret = 0;
|
|
int out_size = 1;
|
|
bool report_data = true;
|
|
|
|
iov_r[0].iov_base = arg;
|
|
iov_r[0].iov_len = sizeof(struct v4l2_ext_controls);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 1, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
ext_controls = (struct v4l2_ext_controls *)in_buf;
|
|
in_buf = (uint8_t *)in_buf + sizeof(struct v4l2_ext_controls);
|
|
in_bufsz -= sizeof(struct v4l2_ext_controls);
|
|
|
|
iov_r[1].iov_base = ext_controls->controls;
|
|
iov_r[1].iov_len = sizeof(struct v4l2_ext_control);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
ext_control = (struct v4l2_ext_control *)in_buf;
|
|
in_buf = (uint8_t *)in_buf + sizeof(struct v4l2_ext_control);
|
|
in_bufsz -= sizeof(struct v4l2_ext_control);
|
|
|
|
iov_w.iov_base = ext_control->ptr;
|
|
|
|
switch (ext_control->id) {
|
|
case V4L2_CID_EXT_VSC_WIN_REGION:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_win_region);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_win_region win;
|
|
ret = v4l2_ext_get_vsc_win_region(fi, &win);
|
|
iov.iov_base = &win;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_win_region);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ZORDER:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_zorder);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_zorder z;
|
|
ret = v4l2_ext_get_vsc_zorder(fi, &z);
|
|
iov.iov_base = &z;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_zorder);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_WIN_PROP:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_win_prop);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_win_prop prop;
|
|
ret = v4l2_ext_get_vsc_win_prop(fi, &prop);
|
|
iov.iov_base = ∝
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_win_prop);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_CONNECT_INFO:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_connect_info);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_connect_info info;
|
|
ret = v4l2_ext_get_vsc_connect_info(fi, &info);
|
|
iov.iov_base = &info;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_connect_info);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_LIMITED_WIN_RATIO:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_scaler_ratio);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_scaler_ratio ratio;
|
|
ret = v4l2_ext_get_vsc_limited_win_ratio(req, &ratio);
|
|
iov.iov_base = ∶
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_scaler_ratio);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_PATTERN:
|
|
iov_w.iov_len = sizeof(enum v4l2_ext_vsc_pattern);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
enum v4l2_ext_vsc_pattern pattern_type;
|
|
ret = v4l2_ext_get_vsc_pattern(req, &pattern_type);
|
|
iov.iov_base = &pattern_type;
|
|
iov.iov_len = sizeof(enum v4l2_ext_vsc_pattern);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_READ_FRAME_BUFFER_INFO:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_pixel_color_info);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_pixel_color_info pixel_color_info;
|
|
ret = v4l2_ext_get_vsc_pixel_color_info(req, &pixel_color_info);
|
|
iov.iov_base = &pixel_color_info;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_pixel_color_info);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ASPECTRATIO_POLICY:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_aspectratio_policy);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_aspectratio_policy policy;
|
|
ret = v4l2_ext_get_vsc_aspectratio_policy(fi, &policy);
|
|
iov.iov_base = &policy;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_aspectratio_policy);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_TIMING_INFO:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_hdmitx_timing_info);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_hdmitx_timing_info timing_info;
|
|
ret = v4l2_ext_get_hdmitx_timing_info(fi, &timing_info);
|
|
iov.iov_base = &timing_info;
|
|
iov.iov_len = sizeof(struct v4l2_ext_hdmitx_timing_info);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_COLOR_ATTR:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_hdmitx_color_attr);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_hdmitx_color_attr color_attr;
|
|
ret = v4l2_ext_get_hdmitx_color_attr(fi, &color_attr);
|
|
iov.iov_base = &color_attr;
|
|
iov.iov_len = sizeof(struct v4l2_ext_hdmitx_color_attr);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_RAW_EDID:
|
|
{
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_hdmitx_edid);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
struct v4l2_ext_hdmitx_edid *edid = (struct v4l2_ext_hdmitx_edid *)in_buf;
|
|
int len = v4l2_ext_get_edid_length(edid->size);
|
|
iov_w.iov_base = edid->pData;
|
|
iov_w.iov_len = len;
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, &iov_w, 1);
|
|
return;
|
|
}
|
|
unsigned char data[512] = { 0 };
|
|
ret = v4l2_cid_get_hdmitx_edid(fi, data);
|
|
iov.iov_base = data;
|
|
iov.iov_len = len;
|
|
break;
|
|
}
|
|
case V4L2_CID_EXT_VSC_DEVICE_CAPABILITY:
|
|
iov_w.iov_len = sizeof(struct v4l2_ext_vsc_device_capability);
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
struct v4l2_ext_vsc_device_capability capability;
|
|
ret = v4l2_ext_get_device_capability(req, &capability);
|
|
iov.iov_base = &capability;
|
|
iov.iov_len = sizeof(struct v4l2_ext_vsc_device_capability);
|
|
break;
|
|
default:
|
|
auto it = ext_ctrls.find(ext_control->id);
|
|
if (it != ext_ctrls.end()) {
|
|
iov_w.iov_len = it->second.data_len;
|
|
if (!out_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, &iov_w, 1);
|
|
return;
|
|
}
|
|
|
|
ret = it->second.get_func(req, fi, out_size);
|
|
report_data = false;
|
|
} else
|
|
out_size = 0;
|
|
break;
|
|
}
|
|
|
|
if (report_data)
|
|
fuse_reply_ioctl_iov(req, ret, &iov, out_size);
|
|
}
|
|
|
|
static void v4l2_s_ext_ctrls(fuse_req_t req, struct fuse_file_info *fi, void *arg,
|
|
const void *in_buf, size_t in_bufsz)
|
|
{
|
|
struct iovec iov_r[3];
|
|
struct v4l2_ext_controls *ext_controls;
|
|
struct v4l2_ext_control *ext_control;
|
|
int ret = 0;
|
|
|
|
iov_r[0].iov_base = arg;
|
|
iov_r[0].iov_len = sizeof(struct v4l2_ext_controls);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 1, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
ext_controls = (struct v4l2_ext_controls *)in_buf;
|
|
in_buf = (uint8_t *)in_buf + sizeof(struct v4l2_ext_controls);
|
|
in_bufsz -= sizeof(struct v4l2_ext_controls);
|
|
|
|
iov_r[1].iov_base = ext_controls->controls;
|
|
iov_r[1].iov_len = sizeof(struct v4l2_ext_control);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 2, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
ext_control = (struct v4l2_ext_control *)in_buf;
|
|
in_buf = (uint8_t *)in_buf + sizeof(struct v4l2_ext_control);
|
|
in_bufsz -= sizeof(struct v4l2_ext_control);
|
|
|
|
switch (ext_control->id) {
|
|
case V4L2_CID_EXT_VSC_WIN_REGION:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_win_region);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_win_region(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ORBIT_WINDOW:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_orbit_window);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_orbit_window(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ORBIT_MOVE:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_orbit_move);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_orbit_move(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ZORDER:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_zorder);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_zorder(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_WIN_PROP:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_win_prop);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_win_prop(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_CONNECT_INFO:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_connect_info);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_connect_info(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_LIMITED_WIN_RATIO:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_scaler_ratio);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_limited_win_ratio(req, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_PATTERN:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(enum v4l2_ext_vsc_pattern);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_pattern(req, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_READ_FRAME_BUFFER_INFO:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_pixel_color_info);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_pixel_color_info(req, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_VSC_ASPECTRATIO_POLICY:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_vsc_aspectratio_policy);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_vsc_aspectratio_policy(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_TIMING_INFO:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_hdmitx_timing_info);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_hdmitx_timing_info(fi, in_buf);
|
|
break;
|
|
case V4L2_CID_EXT_HDMITX_COLOR_ATTR:
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = sizeof(struct v4l2_ext_hdmitx_color_attr);
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = v4l2_ext_set_hdmitx_color_attr(fi, in_buf);
|
|
break;
|
|
default:
|
|
auto it = ext_ctrls.find(ext_control->id);
|
|
if (it != ext_ctrls.end()) {
|
|
iov_r[2].iov_base = ext_control->ptr;
|
|
iov_r[2].iov_len = it->second.data_len;
|
|
if (!in_bufsz) {
|
|
fuse_reply_ioctl_retry(req, iov_r, 3, NULL, 0);
|
|
return;
|
|
}
|
|
ret = it->second.set_func(fi, in_buf);
|
|
}
|
|
break;
|
|
}
|
|
fuse_reply_ioctl_iov(req, ret, NULL, 0);
|
|
}
|
|
|
|
static void v4l2_subscribe_event(fuse_req_t req, void *arg,
|
|
struct fuse_file_info *fi,
|
|
const void *in_buf, size_t in_bufsz, bool state)
|
|
{
|
|
int ret = -1;
|
|
struct v4l2_event_subscription *event = NULL;
|
|
if (!in_bufsz) {
|
|
struct iovec iov_r = { arg, sizeof(struct v4l2_event_subscription) };
|
|
fuse_reply_ioctl_retry(req, &iov_r, 1, NULL, 0);
|
|
return;
|
|
}
|
|
|
|
event = (struct v4l2_event_subscription *)in_buf;
|
|
if (event->type != V4L2_EVENT_CTRL) {
|
|
fuse_reply_err(req, EINVAL);
|
|
return;
|
|
}
|
|
|
|
if (event->id <= V4L2_CID_EXT_VSC_SUBSCRIBE_VIDEO_DELAY &&
|
|
event->id >= V4L2_CID_EXT_VSC_SUBSCRIBE_MUTE_OFF) {
|
|
v4l2_ext_set_subscribe_state(fi, event->id, state);
|
|
ret = 0;
|
|
}
|
|
fuse_reply_ioctl_iov(req, ret, NULL, 0);
|
|
}
|
|
|
|
static void v4l2_dqevent(fuse_req_t req, void *arg,
|
|
struct fuse_file_info *fi, size_t out_bufsz)
|
|
{
|
|
struct v4l2_event event;
|
|
int ret = -1;
|
|
if (!out_bufsz) {
|
|
struct iovec iov_w = { arg, sizeof(struct v4l2_event) };
|
|
fuse_reply_ioctl_retry(req, NULL, 0, &iov_w, 1);
|
|
return;
|
|
}
|
|
|
|
ret = v4l2_ext_get_event(fi, &event);
|
|
|
|
fuse_reply_ioctl(req, ret, &event, sizeof(event));
|
|
}
|
|
|
|
static void scaler_ioctl(fuse_req_t req, int cmd, void *arg,
|
|
struct fuse_file_info *fi, unsigned int flags,
|
|
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
|
{
|
|
static struct v4l2_format fmt;
|
|
memset(&fmt,0,sizeof(fmt));
|
|
|
|
switch (cmd) {
|
|
case VIDIOC_S_FMT:
|
|
if (!in_bufsz || !out_bufsz) {
|
|
struct iovec iov_r = { arg, sizeof(fmt) };
|
|
struct iovec iov_w = { arg, sizeof(fmt) };
|
|
fuse_reply_ioctl_retry(req, &iov_r, 1, &iov_w, 1);
|
|
} else {
|
|
struct v4l2_format *in_fmt = (struct v4l2_format *)in_buf;
|
|
memcpy(&fmt, in_fmt, sizeof(fmt));
|
|
fuse_reply_ioctl(req, 0, &fmt, sizeof(fmt));
|
|
}
|
|
break;
|
|
case VIDIOC_DQEVENT:
|
|
v4l2_dqevent(req, arg, fi, out_bufsz);
|
|
break;
|
|
case VIDIOC_G_CTRL:
|
|
if (!in_bufsz || !out_bufsz) {
|
|
struct iovec iov_r = { arg, sizeof(struct v4l2_control) };
|
|
struct iovec iov_w = { arg, sizeof(struct v4l2_control) };
|
|
fuse_reply_ioctl_retry(req, &iov_r, 1, &iov_w, 1);
|
|
} else {
|
|
v4l2_g_ctrls(req, fi, in_buf);
|
|
}
|
|
break;
|
|
case VIDIOC_S_CTRL:
|
|
if (!in_bufsz) {
|
|
struct iovec iov_r = { arg, sizeof(struct v4l2_control) };
|
|
fuse_reply_ioctl_retry(req, &iov_r, 1, NULL, 0);
|
|
} else {
|
|
v4l2_s_ctrls(req, fi, in_buf);
|
|
}
|
|
break;
|
|
case VIDIOC_G_EXT_CTRLS:
|
|
v4l2_g_ext_ctrls(req, fi, arg, in_buf, in_bufsz, out_bufsz);
|
|
break;
|
|
case VIDIOC_S_EXT_CTRLS:
|
|
v4l2_s_ext_ctrls(req, fi, arg, in_buf, in_bufsz);
|
|
break;
|
|
case VIDIOC_SUBSCRIBE_EVENT:
|
|
v4l2_subscribe_event(req, arg, fi, in_buf, in_bufsz, true);
|
|
break;
|
|
case VIDIOC_UNSUBSCRIBE_EVENT:
|
|
v4l2_subscribe_event(req, arg, fi, in_buf, in_bufsz, false);
|
|
break;
|
|
default:
|
|
fuse_reply_err(req, EINVAL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void scaler_poll(fuse_req_t req, struct fuse_file_info *fi,
|
|
struct fuse_pollhandle *ph)
|
|
{
|
|
video_scaler_file_t *file = (video_scaler_file_t *)fi->fh;
|
|
unsigned revents = 0;
|
|
|
|
if (!file) {
|
|
fuse_reply_poll(req, revents);
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&file->mutex);
|
|
if (ph != NULL) {
|
|
struct fuse_pollhandle *oldph = file->pollHandle;
|
|
if (oldph)
|
|
fuse_pollhandle_destroy(oldph);
|
|
file->pollHandle = ph;
|
|
}
|
|
|
|
if (file->poll_status) {
|
|
revents |= POLLIN;
|
|
file->poll_status = false;
|
|
}
|
|
pthread_mutex_unlock(&file->mutex);
|
|
|
|
fuse_reply_poll(req, revents);
|
|
}
|
|
|
|
static struct cuse_lowlevel_ops scaler0_oper = {
|
|
.open = scaler_open,
|
|
.read = v4l2_read,
|
|
.write = v4l2_write,
|
|
.release = scaler_release,
|
|
.ioctl = scaler_ioctl,
|
|
.poll = scaler_poll,
|
|
};
|
|
|
|
static struct cuse_lowlevel_ops scaler1_oper = {
|
|
.open = scaler_open,
|
|
.read = v4l2_read,
|
|
.write = v4l2_write,
|
|
.release = scaler_release,
|
|
.ioctl = scaler_ioctl,
|
|
.poll = scaler_poll,
|
|
};
|
|
|
|
static struct cuse_lowlevel_ops hdmitx_oper = {
|
|
.open = scaler_open,
|
|
.read = v4l2_read,
|
|
.write = v4l2_write,
|
|
.release = scaler_release,
|
|
.ioctl = scaler_ioctl,
|
|
.poll = scaler_poll,
|
|
};
|
|
|
|
static struct video_scaler_info scaler0 = {
|
|
.display_id = 0,
|
|
.scaler_id = MESON_SCALER_ID0,
|
|
};
|
|
|
|
static struct video_scaler_info scaler1 = {
|
|
.display_id = 0,
|
|
.scaler_id = MESON_SCALER_ID1,
|
|
};
|
|
|
|
static struct video_scaler_info hdmitx = {
|
|
.display_id = 0,
|
|
.scaler_id = VT_HDMITX,
|
|
};
|
|
|
|
void create_vsc_dev(struct fuse_args *args,
|
|
struct cuse_lowlevel_ops *ops, int index, int back_index, void *user_data)
|
|
{
|
|
char dev_name[DEVICE_NAME_LEN] = { 0 };
|
|
char file_name[DEVICE_NAME_LEN] = { 0 };
|
|
const char *dev_info_argv[] = { dev_name };
|
|
struct cuse_info ci;
|
|
int ret;
|
|
|
|
snprintf(dev_name, DEVICE_NAME_LEN, "DEVNAME=video%d", index);
|
|
snprintf(file_name, DEVICE_NAME_LEN, "/dev/video%d", index);
|
|
|
|
if (access(file_name, R_OK | W_OK) == 0)
|
|
snprintf(dev_name, DEVICE_NAME_LEN, "DEVNAME=video%d", back_index);
|
|
|
|
memset(&ci, 0, sizeof(ci));
|
|
ci.dev_major = 0;
|
|
ci.dev_minor = 0;
|
|
ci.dev_info_argc = 1;
|
|
ci.dev_info_argv = dev_info_argv;
|
|
ci.flags = CUSE_UNRESTRICTED_IOCTL;
|
|
|
|
ret = cuse_lowlevel_main(args->argc, args->argv, &ci, ops, user_data);
|
|
if (ret != 0)
|
|
printf("create vsc%d failed\n", index);
|
|
}
|
|
|
|
void create_hdmitx_dev(struct fuse_args *args, struct cuse_lowlevel_ops *ops, void *user_data)
|
|
{
|
|
char dev_name_hdmitx[DEVICE_NAME_LEN] = "DEVNAME=video15";
|
|
const char *dev_info_argv_hdmitx[] = { dev_name_hdmitx };
|
|
struct cuse_info ci_hdmitx;
|
|
int ret;
|
|
|
|
if (access(V4L2_EXT_DEV_PATH_HDMITX, R_OK | W_OK) == 0)
|
|
memcpy(dev_name_hdmitx, DEV_VIDEO_32, sizeof(DEV_VIDEO_32));
|
|
|
|
memset(&ci_hdmitx, 0, sizeof(ci_hdmitx));
|
|
ci_hdmitx.dev_major = 0;
|
|
ci_hdmitx.dev_minor = 0;
|
|
ci_hdmitx.dev_info_argc = 1;
|
|
ci_hdmitx.dev_info_argv = dev_info_argv_hdmitx;
|
|
ci_hdmitx.flags = CUSE_UNRESTRICTED_IOCTL;
|
|
|
|
ret = cuse_lowlevel_main(args->argc, args->argv, &ci_hdmitx, ops, user_data);
|
|
if (ret != 0)
|
|
printf("create hdmitx failed\n");
|
|
}
|
|
|
|
void sig_child(void)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
|
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
|
|
{
|
|
fprintf(stderr, "wait child success\n");
|
|
}
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int status, i;
|
|
|
|
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
|
|
|
signal(SIGCHLD, (__sighandler_t)sig_child);
|
|
|
|
for (i = 0; i < VT_SCALER_MAX; i++)
|
|
{
|
|
status = fork();
|
|
if (status == 0 || status == -1)
|
|
break;
|
|
}
|
|
|
|
if (status == -1)
|
|
{
|
|
fprintf(stderr, "fork error\n");
|
|
exit(-1);
|
|
}
|
|
else if (status == 0)
|
|
{
|
|
if (i == VT_SCALER_0)
|
|
create_vsc_dev(&args, &scaler0_oper,
|
|
V4L2_EXT_DEV_NO_SCALER0,
|
|
DEV_VIDEO_100,
|
|
(void *)&scaler0);
|
|
if (i == VT_SCALER_1)
|
|
create_vsc_dev(&args, &scaler1_oper,
|
|
V4L2_EXT_DEV_NO_SCALER1,
|
|
DEV_VIDEO_101,
|
|
(void *)&scaler1);
|
|
if (i == VT_HDMITX)
|
|
create_hdmitx_dev(&args, &hdmitx_oper, (void *)&hdmitx);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "fork start\n");
|
|
while (1)
|
|
{
|
|
sleep(1);
|
|
}
|
|
}
|
|
fprintf(stderr, "two thread exit\n");
|
|
return 0;
|
|
}
|