/* * Copyright (c) 2021 Rockchip Electronics Co., Ltd */ #define LOG_TAG "V4L2DeviceEvent" #include <cutils/log.h> #include <sys/stat.h> #include <fcntl.h> #include <poll.h> #include <unistd.h> #include "TvDeviceV4L2Event.h" using android::UNKNOWN_ERROR; using android::NO_ERROR; //////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////// V4L2DeviceEvent::V4L2DeviceEvent() { } V4L2DeviceEvent::~V4L2DeviceEvent() { ALOGI("@%s", __FUNCTION__); if (mFd != -1) { ALOGW("Destroying a device object not closed, closing first"); } } int V4L2DeviceEvent::initialize(int fd){ mFd = fd; subscribeEvent(V4L2_EVENT_SOURCE_CHANGE); subscribeEvent(V4L2_EVENT_CTRL); subscribeEvent(RK_HDMIRX_V4L2_EVENT_SIGNAL_LOST); mV4L2EventThread = new V4L2EventThread(mFd,callback_); mV4L2EventThread->v4l2pipe(); mV4L2EventThread->run("Tif_Ev", android::PRIORITY_DISPLAY); return 0; } void V4L2DeviceEvent::closeEventThread() { ALOGW("@%s start", __FUNCTION__); if (mV4L2EventThread) { mV4L2EventThread->requestExit(); mV4L2EventThread->join(); mV4L2EventThread.clear(); } ALOGW("@%s end", __FUNCTION__); } void V4L2DeviceEvent::closePipe() { ALOGW("@%s start", __FUNCTION__); if (mV4L2EventThread) { mV4L2EventThread->closeDevice(); } ALOGW("@%s end", __FUNCTION__); } int V4L2DeviceEvent::subscribeEvent(int event) { ALOGI("@%s", __FUNCTION__); int ret(0); struct v4l2_event_subscription sub; if (mFd == -1) { ALOGW("Device %d already closed. cannot subscribe.",mFd); return -1; } CLEAR(sub); sub.type = event; if(event == V4L2_EVENT_CTRL)sub.id = V4L2_CID_DV_RX_POWER_PRESENT; ret = ioctl(mFd, VIDIOC_SUBSCRIBE_EVENT, &sub); if (ret < 0) { ALOGE("error subscribing event %x: %s", event, strerror(errno)); return ret; } return ret; } int V4L2DeviceEvent::unsubscribeEvent(int event) { ALOGI("@%s", __FUNCTION__); int ret(0); struct v4l2_event_subscription sub; if (mFd == -1) { ALOGW("Device %d closed. cannot unsubscribe.", mFd); return -1; } CLEAR(sub); sub.type = event; ret = ioctl(mFd, VIDIOC_UNSUBSCRIBE_EVENT, &sub); if (ret < 0) { ALOGE("error unsubscribing event %x :%s",event,strerror(errno)); return ret; } return ret; } int V4L2DeviceEvent::dequeueEvent(struct v4l2_event *event) { ALOGD("@%s", __FUNCTION__); int ret(0); if (mFd == -1) { ALOGW("Device %d closed. cannot dequeue event.", mFd); return -1; } ret = ioctl(mFd, VIDIOC_DQEVENT, event); if (ret < 0) { ALOGE("error dequeuing event"); return ret; } return ret; } status_t V4L2DeviceEvent::setControl(int aControlNum, const int value, const char *name) { ALOGD("@%s", __FUNCTION__); struct v4l2_control control; struct v4l2_ext_controls controls; struct v4l2_ext_control ext_control; CLEAR(control); CLEAR(controls); CLEAR(ext_control); ALOGD("setting attribute [%s] to %d", name, value); if (mFd == -1) { ALOGE("%s: Invalid device state (CLOSED)", __FUNCTION__); return UNKNOWN_ERROR; } control.id = aControlNum; control.value = value; controls.ctrl_class = V4L2_CTRL_ID2CLASS(control.id); controls.count = 1; controls.controls = &ext_control; ext_control.id = aControlNum; ext_control.value = value; if (ioctl(mFd, VIDIOC_S_EXT_CTRLS, &controls) == 0) return NO_ERROR; if (ioctl(mFd, VIDIOC_S_CTRL, &control) == 0) return NO_ERROR; ALOGE("Failed to set value %d for control %s (%d) on device , %s", value, name, aControlNum, strerror(errno)); return UNKNOWN_ERROR; } status_t V4L2DeviceEvent::getControl(int aControlNum, int *value) { ALOGD("@%s", __FUNCTION__); struct v4l2_control control; struct v4l2_ext_controls controls; struct v4l2_ext_control ext_control; CLEAR(control); CLEAR(controls); CLEAR(ext_control); if (mFd == -1) { ALOGE("%s: Invalid state device (CLOSED)", __FUNCTION__); return UNKNOWN_ERROR; } control.id = aControlNum; controls.ctrl_class = V4L2_CTRL_ID2CLASS(control.id); controls.count = 1; controls.controls = &ext_control; ext_control.id = aControlNum; if (ioctl(mFd, VIDIOC_G_EXT_CTRLS, &controls) == 0) { *value = ext_control.value; return NO_ERROR; } if (ioctl(mFd, VIDIOC_G_CTRL, &control) == 0) { *value = control.value; return NO_ERROR; } ALOGE("Failed to get value for control (%d) on device, %s", aControlNum, strerror(errno)); return UNKNOWN_ERROR; } status_t V4L2DeviceEvent::queryMenu(v4l2_querymenu &menu) { ALOGD("@%s", __FUNCTION__); if (mFd == -1) { ALOGE("%s: Invalid state device (CLOSED)", __FUNCTION__); return UNKNOWN_ERROR; } if (ioctl(mFd, VIDIOC_QUERYMENU, &menu) == 0) { return NO_ERROR; } ALOGE("Failed to get values for query menu (%d) on device , %s", menu.id, strerror(errno)); return UNKNOWN_ERROR; } status_t V4L2DeviceEvent::queryControl(v4l2_queryctrl &control) { ALOGD("@%s", __FUNCTION__); if (mFd == -1) { ALOGE("%s: Invalid state device (CLOSED)", __FUNCTION__); return UNKNOWN_ERROR; } if (ioctl(mFd, VIDIOC_QUERYCTRL, &control) == 0) { return NO_ERROR; } ALOGE("Failed to get values for query control (%d) on device, %s", control.id, strerror(errno)); return UNKNOWN_ERROR; } V4L2DeviceEvent::V4L2EventThread::V4L2EventThread(int fd,V4L2EventCallBack callback){ mVideoFd = fd; mCallback_ = callback; mCurformat = new V4L2DeviceEvent::FormartSize(0,0,0); mStopThread = false; } V4L2DeviceEvent::V4L2EventThread::~V4L2EventThread() { closeDevice(); } bool V4L2DeviceEvent::V4L2EventThread::v4l2pipe() { ALOGI("@%s", __FUNCTION__); if (pipe(pipefd) < 0) { ALOGE("pipe failed: %s\n", strerror(errno)); return false; } return true; } void V4L2DeviceEvent::V4L2EventThread::openDevice() { char video_name[64]; memset(video_name, 0, sizeof(video_name)); strcat(video_name, "/dev/v4l-subdev2"); } /*int V4L2DeviceEvent::V4L2EventThread::getHdmiIn(bool enforce){ if(enforce && mCurformat->getIsHdmiIn()) return mCurformat->getIsHdmiIn(); struct v4l2_control control; memset(&control, 0, sizeof(struct v4l2_control)); control.id = V4L2_CID_DV_RX_POWER_PRESENT; int err = ioctl(mVideoFd, VIDIOC_G_CTRL, &control); if (err < 0) { ALOGV("Set POWER_PRESENT failed ,%d(%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } unsigned int noSignalAndSync = 0; ioctl(mVideoFd, VIDIOC_G_INPUT, &noSignalAndSync); ALOGI("noSignalAndSync ? %s",noSignalAndSync?"YES":"NO"); mCurformat->setIsHdmiIn(control.value && !noSignalAndSync); ALOGD("======================= ,%d",mCurformat->getIsHdmiIn()); return mCurformat->getIsHdmiIn(); } int V4L2DeviceEvent::V4L2EventThread::getFormat(){ struct v4l2_dv_timings dv_timings; memset(&dv_timings, 0 ,sizeof(struct v4l2_dv_timings)); int err = ioctl(mVideoFd, VIDIOC_SUBDEV_QUERY_DV_TIMINGS, &dv_timings); if (err < 0) { ALOGD("Set VIDIOC_SUBDEV_QUERY_DV_TIMINGS failed ,%d(%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } sp<V4L2DeviceEvent::FormartSize> formatSize = new V4L2DeviceEvent::FormartSize(dv_timings.bt.width,dv_timings.bt.height,true); //ALOGD("FormatWeight=%d,FormatHeigh=%d,%d",formatSize->getFormatWeight(),formatSize->getFormatHeight(),formatSize->getIsHdmiIn()); if(mCurformat->getFormatWeight() != formatSize->getFormatWeight() && mCurformat->getFormatHeight() != formatSize->getFormatHeight()){ getHdmiIn(false); if(mCallback_ != NULL) mCallback_(formatSize->getFormatWeight(),formatSize->getFormatHeight(),mCurformat->getFormat(),mCurformat->getIsHdmiIn()); mCurformat->setFormatWeight(formatSize->getFormatWeight()); mCurformat->setFormatHeight(formatSize->getFormatHeight()); } return NO_ERROR; }*/ void V4L2DeviceEvent::V4L2EventThread::closeDevice() { ALOGI("close device"); if (write(pipefd[1], "q", 1) != 1) {} close(pipefd[0]); close(pipefd[1]); mStopThread = true; } bool V4L2DeviceEvent::V4L2EventThread::threadLoop() { ALOGV("@%s", __FUNCTION__); struct pollfd fds[2]; //int retry = 3; //fds.events = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLRDBAND | POLLPRI; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; fds[1].fd = mVideoFd; fds[1].events = POLLPRI; struct v4l2_event ev; CLEAR(ev); if (poll(fds, 2, 5000) < 0) { ALOGD("%d: poll failed: %s\n", mVideoFd, strerror(errno)); return false; } if (fds[0].revents & POLLIN) { ALOGD("%d: quit message received\n", mVideoFd); return false; } if (fds[1].revents & POLLPRI) { if (ioctl(fds[1].fd, VIDIOC_DQEVENT, &ev) == 0) { switch (ev.type) { case V4L2_EVENT_SOURCE_CHANGE: ALOGD("%d: V4L2_EVENT_SOURCE_CHANGE event\n", mVideoFd); /*retry = 3; while(retry-- >= 0){ if(!getFormat()) break; else usleep(100*1000); }*/ break; case V4L2_EVENT_CTRL: ALOGD("%d: V4L2_EVENT_CTRL event \n", mVideoFd ); //getHdmiIn(false); break; default: ALOGD("%d: unknown event\n", mVideoFd); break; } if(mCallback_ != NULL) mCallback_(ev.type); } else { ALOGD("%d: VIDIOC_DQEVENT failed: %s\n",mVideoFd, strerror(errno)); } } if (mStopThread) { return false; } return true; } //////////////////////////////////////////////////////////////////// // PRIVATE METHODS ////////////////////////////////////////////////////////////////////