Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/meson_videoserver/vscfuse/vsc_fuse.cpp
T

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 = &prop;
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 = &ratio;
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;
}