android13/hardware/rockchip/camera/common/v4l2dev/v4l2devicebase.cpp

449 lines
12 KiB
C++

/*
* Copyright (C) 2013-2017 Intel Corporation
* Copyright (c) 2017, Fuzhou Rockchip Electronics Co., Ltd
*
* 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 LOG_TAG "V4L2DevBase"
#include "LogHelper.h"
#include "v4l2device.h"
#include "UtilityMacros.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
////////////////////////////////////////////////////////////////////
NAMESPACE_DECLARATION {
V4L2DeviceBase::V4L2DeviceBase(const char *name): mName(name),
mFd(-1)
{
}
V4L2DeviceBase::~V4L2DeviceBase()
{
LOGI("@%s", __FUNCTION__);
if (mFd != -1) {
LOGW("Destroying a device object not closed, closing first");
close();
}
}
status_t V4L2DeviceBase::open()
{
LOGI("@%s %s", __FUNCTION__, mName.c_str());
status_t ret = NO_ERROR;
struct stat st;
CLEAR(st);
if (mFd != -1) {
LOGE("Trying to open a device already open");
return NO_ERROR; //INVALID_OPERATION;
}
if (stat (mName.c_str(), &st) == -1) {
LOGE("Error stat video device %s: %s",
mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
if (!S_ISCHR (st.st_mode)) {
LOGE("%s is not a device", mName.c_str());
return UNKNOWN_ERROR;
}
PERFORMANCE_ATRACE_NAME_SNPRINTF("Open - %s", mName.c_str());
perfopen(mName.c_str(), O_RDWR, mFd);
if (mFd < 0) {
LOGE("Error opening video device %s: %s",
mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
return ret;
}
status_t V4L2DeviceBase::close()
{
LOGI("@%s device : %s", __FUNCTION__, mName.c_str());
if (mFd == -1) {
LOGW("Device not opened!");
return INVALID_OPERATION;
}
if (perfclose(mName.c_str(), mFd) < 0) {
LOGE("Close video device failed: %s", strerror(errno));
return UNKNOWN_ERROR;
}
mFd = -1;
return NO_ERROR;
}
int V4L2DeviceBase::xioctl(int request, void *arg, int *errnoCopy) const
{
int ret(0);
if (mFd == -1) {
LOGE("%s invalid device closed!",__FUNCTION__);
return INVALID_OPERATION;
}
do {
ret = pioctl (mFd, request, arg, mName.c_str());
} while (-1 == ret && EINTR == errno);
if (ret < 0) {
if (errnoCopy != nullptr)
*errnoCopy = errno;
LOGW ("%s: Request 0x%x failed: %s", __FUNCTION__, request, strerror(errno));
}
return ret;
}
/**
* Waits for frame data to be available
*
* \param timeout time to wait for data (in ms), timeout of -1
* means to wait indefinitely for data
*
* \return 0: timeout, -1: error happened, positive number: success
*/
int V4L2DeviceBase::poll(int timeout)
{
struct pollfd pfd = {0,0,0};
int ret(0);
if (mFd == -1) {
LOGW("Device %s already closed. Do nothing.", mName.c_str());
return -1;
}
pfd.fd = mFd;
pfd.events = POLLPRI | POLLIN | POLLERR;
ret = perfpoll(&pfd, 1, timeout);
if (ret < 0) {
LOGE("poll error ret=%d, mFd=%d, error:%s", ret, mFd, strerror(errno));
return ret;
}
if (pfd.revents & POLLERR) {
LOGE("%s received POLLERR", __FUNCTION__);
return -1;
}
return ret;
}
int V4L2DeviceBase::subscribeEvent(int event)
{
LOGI("@%s", __FUNCTION__);
int ret(0);
struct v4l2_event_subscription sub;
if (mFd == -1) {
LOGW("Device %s already closed. cannot subscribe.", mName.c_str());
return -1;
}
CLEAR(sub);
sub.type = event;
ret = pioctl(mFd, VIDIOC_SUBSCRIBE_EVENT, &sub, mName.c_str());
if (ret < 0) {
LOGE("error subscribing event %x: %s", event, strerror(errno));
return ret;
}
return ret;
}
int V4L2DeviceBase::unsubscribeEvent(int event)
{
LOGI("@%s", __FUNCTION__);
int ret(0);
struct v4l2_event_subscription sub;
if (mFd == -1) {
LOGW("Device %s closed. cannot unsubscribe.", mName.c_str());
return -1;
}
CLEAR(sub);
sub.type = event;
ret = pioctl(mFd, VIDIOC_UNSUBSCRIBE_EVENT, &sub, mName.c_str());
if (ret < 0) {
LOGE("error unsubscribing event %x :%s",event,strerror(errno));
return ret;
}
return ret;
}
int V4L2DeviceBase::dequeueEvent(struct v4l2_event *event)
{
LOGD("@%s", __FUNCTION__);
int ret(0);
if (mFd == -1) {
LOGW("Device %s closed. cannot dequeue event.", mName.c_str());
return -1;
}
ret = pioctl(mFd, VIDIOC_DQEVENT, event, mName.c_str());
if (ret < 0) {
LOGE("error dequeuing event");
return ret;
}
return ret;
}
status_t V4L2DeviceBase::setControl(int aControlNum, const int value, const char *name)
{
LOGD("@%s", __FUNCTION__);
struct v4l2_control control;
struct v4l2_ext_controls controls;
struct v4l2_ext_control ext_control;
CLEAR(control);
CLEAR(controls);
CLEAR(ext_control);
LOGD("setting attribute [%s] to %d", name, value);
if (mFd == -1) {
LOGE("%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 (pioctl(mFd, VIDIOC_S_EXT_CTRLS, &controls, mName.c_str()) == 0)
return NO_ERROR;
if (pioctl(mFd, VIDIOC_S_CTRL, &control, mName.c_str()) == 0)
return NO_ERROR;
LOGE("Failed to set value %d for control %s (%d) on device '%s', %s",
value, name, aControlNum, mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
status_t V4L2DeviceBase::getControl(int aControlNum, int *value)
{
LOGD("@%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) {
LOGE("%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 (pioctl(mFd, VIDIOC_G_EXT_CTRLS, &controls, mName.c_str()) == 0) {
*value = ext_control.value;
return NO_ERROR;
}
if (pioctl(mFd, VIDIOC_G_CTRL, &control, mName.c_str()) == 0) {
*value = control.value;
return NO_ERROR;
}
LOGE("Failed to get value for control (%d) on device '%s', %s",
aControlNum, mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
status_t V4L2DeviceBase::queryMenu(v4l2_querymenu &menu)
{
LOGD("@%s", __FUNCTION__);
if (mFd == -1) {
LOGE("%s: Invalid state device (CLOSED)", __FUNCTION__);
return UNKNOWN_ERROR;
}
if (pioctl(mFd, VIDIOC_QUERYMENU, &menu, mName.c_str()) == 0) {
return NO_ERROR;
}
LOGE("Failed to get values for query menu (%d) on device '%s', %s",
menu.id, mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
status_t V4L2DeviceBase::queryControl(v4l2_queryctrl &control)
{
LOGD("@%s", __FUNCTION__);
if (mFd == -1) {
LOGE("%s: Invalid state device (CLOSED)", __FUNCTION__);
return UNKNOWN_ERROR;
}
if (pioctl(mFd, VIDIOC_QUERYCTRL, &control, mName.c_str()) == 0) {
return NO_ERROR;
}
LOGE("Failed to get values for query control (%d) on device '%s', %s",
control.id, mName.c_str(), strerror(errno));
return UNKNOWN_ERROR;
}
/**
* \brief Poll V4L2 devices
*
* \param devices [IN] devices to poll()
* \param activeDevices [OUT] devices that had data available
* \param inactiveDevices [OUT] devices that did not have data available
* \param timeOut [IN] poll timeout
* \param flushFd [IN] file descriptor of the pipe device that will be used
* to return from poll() in case of flush request,
* i.e., to abort poll before timeout
*/
int V4L2DeviceBase::pollDevices(const std::vector<std::shared_ptr<V4L2DeviceBase>> &devices,
std::vector<std::shared_ptr<V4L2DeviceBase>> &activeDevices,
std::vector<std::shared_ptr<V4L2DeviceBase>> &inactiveDevices,
int timeOut, int flushFd, int events)
{
LOGD("@%s", __FUNCTION__);
int numFds = devices.size();
int totalNumFds = (flushFd != -1) ? numFds + 1 : numFds; //adding one more fd if flushfd given.
struct pollfd pollFds[totalNumFds];
int ret = 0;
for (int i = 0; i < numFds; i++) {
pollFds[i].fd = devices[i]->mFd;
pollFds[i].events = events | POLLERR; // we always poll for errors, asked or not
pollFds[i].revents = 0;
}
if (flushFd != -1) {
pollFds[numFds].fd = flushFd;
pollFds[numFds].events = POLLPRI | POLLIN;
pollFds[numFds].revents = 0;
}
ret = perfpoll(pollFds, totalNumFds, timeOut);
if (ret <= 0) {
for (uint32_t i = 0; i < devices.size(); i++) {
LOGE("Device %s poll failed (%s)", devices[i]->name(),
(ret == 0) ? "timeout" : "error");
if (pollFds[i].revents & POLLERR) {
LOGE("%s: device %s received POLLERR", __FUNCTION__, devices[i]->name());
}
}
return ret;
}
activeDevices.clear();
inactiveDevices.clear();
//check first the flush
if (flushFd != -1) {
if ((pollFds[numFds].revents & POLLIN) || (pollFds[numFds].revents & POLLPRI)) {
LOGI("%s: Poll returning from flush", __FUNCTION__);
return ret;
}
}
// check other active devices.
for (int i = 0; i < numFds; i++) {
if (pollFds[i].revents & POLLERR) {
LOGE("%s: received POLLERR", __FUNCTION__);
return -1;
}
// return nodes that have data available
if (pollFds[i].revents & events) {
activeDevices.push_back(devices[i]);
} else
inactiveDevices.push_back(devices[i]);
}
return ret;
}
int V4L2DeviceBase::frmsizeWidth(struct v4l2_frmsizeenum &size)
{
return size.type == V4L2_FRMSIZE_TYPE_DISCRETE ?
size.discrete.width :
size.stepwise.max_width;
}
int V4L2DeviceBase::frmsizeHeight(struct v4l2_frmsizeenum &size)
{
return size.type == V4L2_FRMSIZE_TYPE_DISCRETE ?
size.discrete.height :
size.stepwise.max_height;
}
void V4L2DeviceBase::frmivalIval(struct v4l2_frmivalenum &frmival, struct v4l2_fract &ival)
{
if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
ival = frmival.discrete;
} else {
ival = frmival.stepwise.min;
}
}
int V4L2DeviceBase::cmpFract(struct v4l2_fract &f1, struct v4l2_fract &f2)
{
return f1.numerator * f2.denominator - f2.numerator * f1.denominator;
}
int V4L2DeviceBase::cmpIval(struct v4l2_frmivalenum &i1, struct v4l2_frmivalenum &i2)
{
struct v4l2_fract f1, f2;
frmivalIval(i1, f1);
frmivalIval(i2, f2);
return cmpFract(f1, f2);
}
} NAMESPACE_DECLARATION_END
////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
////////////////////////////////////////////////////////////////////