/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vsc_fuse.h" #include "v4l2-controls-ext_func.h" #include "BinderClient.h" #include #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 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; }