From patchwork Sun Jun 2 13:04:33 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1333 X-Patchwork-Delegate: jacopo@jmondi.org Return-Path: Received: from relay11.mail.gandi.net (relay11.mail.gandi.net [217.70.178.231]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3C6F760BFA for ; Sun, 2 Jun 2019 15:03:48 +0200 (CEST) Received: from uno.homenet.telecomitalia.it (unknown [79.35.79.123]) (Authenticated sender: jacopo@jmondi.org) by relay11.mail.gandi.net (Postfix) with ESMTPSA id 4992F100005; Sun, 2 Jun 2019 13:03:47 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Sun, 2 Jun 2019 15:04:33 +0200 Message-Id: <20190602130435.18780-5-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190602130435.18780-1-jacopo@jmondi.org> References: <20190602130435.18780-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 4/6] libcamera: v4l2_base: Add V4L2 control support X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 02 Jun 2019 13:03:48 -0000 Implement operations to set and get V4L2 controls in the V4L2Base class. The operations allow to set and get a single or a list of controls. Signed-off-by: Jacopo Mondi --- src/libcamera/include/v4l2_base.h | 12 ++ src/libcamera/v4l2_base.cpp | 290 ++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) diff --git a/src/libcamera/include/v4l2_base.h b/src/libcamera/include/v4l2_base.h index 2fda81a960d2..7d4e238dadfa 100644 --- a/src/libcamera/include/v4l2_base.h +++ b/src/libcamera/include/v4l2_base.h @@ -9,6 +9,8 @@ #include +#include + namespace libcamera { class V4L2Base @@ -22,8 +24,18 @@ public: virtual void close() = 0; bool isOpen() const; + std::vector getControls(std::vector &ctrl_ids); + V4L2Control *getControl(unsigned int ctrl_id); + int setControls(std::vector &ctrls); + int setControl(V4L2Control *ctrl); + protected: int fd_; + +private: + int queryControl(unsigned int ctrl_id, + struct v4l2_query_ext_ctrl *qext_ctrl); + }; }; /* namespace libcamera */ diff --git a/src/libcamera/v4l2_base.cpp b/src/libcamera/v4l2_base.cpp index 7d05a3c82e4d..423a4d84e276 100644 --- a/src/libcamera/v4l2_base.cpp +++ b/src/libcamera/v4l2_base.cpp @@ -5,7 +5,13 @@ * v4l2_base.cpp - Common base for V4L2 devices and subdevices */ +#include +#include +#include + +#include "log.h" #include "v4l2_base.h" +#include "v4l2_controls.h" /** * \file v4l2_base.h @@ -14,6 +20,8 @@ namespace libcamera { +LOG_DEFINE_CATEGORY(V4L2Base) + /** * \class V4L2Base * \brief Base class for V4L2Device and V4L2Subdevice @@ -31,6 +39,7 @@ namespace libcamera { */ /** + * \fn V4L2Base::open() * \brief Open a V4L2 device or subdevice * * Pure virtual method to be implemented by the derived classes. @@ -39,6 +48,7 @@ namespace libcamera { */ /** + * \fn V4L2Base::close() * \brief Close the device or subdevice * * Pure virtual method to be implemented by the derived classes. @@ -53,6 +63,286 @@ bool V4L2Base::isOpen() const return fd_ != -1; } +int V4L2Base::queryControl(unsigned int id, + struct v4l2_query_ext_ctrl *qext_ctrl) +{ + qext_ctrl->id = id; + + int ret = ioctl(fd_, VIDIOC_QUERY_EXT_CTRL, qext_ctrl); + if (ret) { + ret = -errno; + LOG(V4L2Base, Error) + << "Unable to query extended control 0x" + << std::hex << qext_ctrl->id + << ": " << strerror(-ret); + return ret; + } + + if (qext_ctrl->flags & V4L2_CTRL_FLAG_DISABLED) { + LOG(V4L2Base, Error) + << "Extended control 0x" << std::hex + << qext_ctrl->id << " is disabled"; + return -EINVAL; + } + + return 0; +} + +/** + * \brief Get values of a list of control IDs + * \param ctrl_ids The list of control IDs to retrieve value of + * \return A list of V4L2Control on success or a empty list otherwise + */ +std::vector V4L2Base::getControls(std::vector &ctrl_ids) +{ + unsigned int count = ctrl_ids.size(); + if (!count) + return std::vector {}; + + struct v4l2_ext_control *v4l2_ctrls = static_cast + (new struct v4l2_ext_control[count]); + if (!v4l2_ctrls) + return std::vector {}; + + /* + * Query each control in the vector to get back its type and expected + * size in order to get its current value. + */ + for (unsigned int i = 0; i < count; ++i) { + struct v4l2_ext_control *v4l2_ctrl = &v4l2_ctrls[i]; + unsigned int ctrl_id = ctrl_ids[i]; + struct v4l2_query_ext_ctrl qext_ctrl = {}; + + int ret = queryControl(ctrl_id, &qext_ctrl); + if (ret) + return std::vector {}; + + v4l2_ctrl->id = ctrl_id; + v4l2_ctrl->size = qext_ctrl.elem_size * qext_ctrl.elems; + + /* Reserve memory for controls with payload. */ + if (!(qext_ctrl.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD)) + continue; + + switch (qext_ctrl.type) { + case V4L2_CTRL_TYPE_U8: + v4l2_ctrl->p_u8 = static_cast + (new uint8_t[v4l2_ctrl->size]); + break; + case V4L2_CTRL_TYPE_U16: + v4l2_ctrl->p_u16 = static_cast + (new uint16_t[v4l2_ctrl->size]); + break; + case V4L2_CTRL_TYPE_U32: + v4l2_ctrl->p_u32 = static_cast + (new uint32_t[v4l2_ctrl->size]); + break; + } + } + + struct v4l2_ext_controls v4l2_ext_ctrls = {}; + v4l2_ext_ctrls.which = V4L2_CTRL_WHICH_CUR_VAL; + v4l2_ext_ctrls.count = count; + v4l2_ext_ctrls.controls = v4l2_ctrls; + + int ret = ioctl(fd_, VIDIOC_G_EXT_CTRLS, &v4l2_ext_ctrls); + if (ret) { + ret = -errno; + LOG(V4L2Base, Error) + << "Unable to get extended controls: " << strerror(-ret); + return std::vector {}; + } + + /* + * For each requested control, create a V4L2Control which contains + * the current value and store it in the vector returned to the caller. + */ + std::vector controls = {}; + for (unsigned int i = 0; i < count; ++i) { + struct v4l2_ext_control *v4l2_ctrl = &v4l2_ctrls[i]; + struct v4l2_query_ext_ctrl qext_ctrl = {}; + + int ret = queryControl(v4l2_ctrl->id, &qext_ctrl); + if (ret) + return std::vector {}; + + unsigned int size = qext_ctrl.elem_size * qext_ctrl.elem_size; + if (qext_ctrl.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { + /* Control types with payload */ + switch (qext_ctrl.type) { + case V4L2_CTRL_TYPE_U8: + { + V4L2U8Control *c = + new V4L2U8Control(v4l2_ctrl->id, size, + v4l2_ctrl->p_u8); + controls.push_back(c); + } + break; + case V4L2_CTRL_TYPE_U16: + { + V4L2U16Control *c = + new V4L2U16Control(v4l2_ctrl->id, size, + v4l2_ctrl->p_u16); + controls.push_back(c); + } + break; + case V4L2_CTRL_TYPE_U32: + { + V4L2U32Control *c = + new V4L2U32Control(v4l2_ctrl->id, size, + v4l2_ctrl->p_u32); + controls.push_back(c); + } + break; + default: + LOG(V4L2Base, Error) + << "Compound control types not supported: " + << std::hex << qext_ctrl.type; + return std::vector {}; + } + } else { + /* Control types without payload. */ + switch (qext_ctrl.type) { + case V4L2_CTRL_TYPE_INTEGER64: + { + V4L2Int64Control *c = + new V4L2Int64Control(v4l2_ctrl->id, + v4l2_ctrl->value64); + controls.push_back(c); + } + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BUTTON: + { + V4L2IntControl *c = + new V4L2IntControl(v4l2_ctrl->id, + v4l2_ctrl->value); + controls.push_back(c); + } + break; + default: + LOG(V4L2Base, Error) + << "Invalid non-compound control type :" + << std::hex << qext_ctrl.type; + return std::vector {}; + } + } + } + + return controls; +} + +/** + * \brief Get the value of control with id \a ctrl_id + * \param ctrl_id The control id + * \return The V4L2Control which represent the value of the control on success + * nullptr otherwise + */ +V4L2Control *V4L2Base::getControl(unsigned int ctrl_id) +{ + std::vector v = { ctrl_id, }; + std::vector ctrls = getControls(v); + + if (ctrls.empty()) + return nullptr; + + return ctrls[0]; +} + +/** + * \brief Apply a list of controls to the device + * \param ctrls The list of controls to apply + * \return 0 on success or a negative error code otherwise + */ +int V4L2Base::setControls(std::vector &ctrls) +{ + unsigned int count = ctrls.size(); + if (!count) + return -EINVAL; + + struct v4l2_ext_control *v4l2_ctrls = static_cast + (new struct v4l2_ext_control[count]); + if (!v4l2_ctrls) + return -ENOMEM; + + /* + * Query each control in the vector to get back its type and expected + * size and set the new desired value in each v4l2_ext_control member. + */ + for (unsigned int i = 0; i < count; ++i) { + struct v4l2_ext_control *v4l2_ctrl = &v4l2_ctrls[i]; + struct v4l2_query_ext_ctrl qext_ctrl = {}; + V4L2Control *ctrl = ctrls[i]; + + int ret = queryControl(ctrl->id(), &qext_ctrl); + if (ret) + return ret; + + v4l2_ctrl->id = ctrl->id(); + v4l2_ctrl->size = qext_ctrl.elem_size * qext_ctrl.elems; + + if (qext_ctrl.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD) { + /** \todo: Support set controls with payload. */ + LOG(V4L2Base, Error) + << "Controls with payload not supported"; + return -EINVAL; + } else { + switch (qext_ctrl.type) { + case V4L2_CTRL_TYPE_INTEGER64: + { + V4L2Int64Control *c = + static_cast(ctrl); + v4l2_ctrl->value64 = c->value(); + } + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BUTTON: + { + V4L2IntControl *c = + static_cast(ctrl); + v4l2_ctrl->value = c->value(); + } + break; + default: + LOG(V4L2Base, Error) + << "Invalid non-compound control type :" + << qext_ctrl.type; + return -EINVAL; + } + } + } + + struct v4l2_ext_controls v4l2_ext_ctrls = {}; + v4l2_ext_ctrls.which = V4L2_CTRL_WHICH_CUR_VAL; + v4l2_ext_ctrls.count = count; + v4l2_ext_ctrls.controls = v4l2_ctrls; + + int ret = ioctl(fd_, VIDIOC_S_EXT_CTRLS, &v4l2_ext_ctrls); + if (ret) { + ret = -errno; + LOG(V4L2Base, Error) + << "Unable to set extended controls: " << strerror(-ret); + return ret; + } + + return 0; +} + +/** + * \brief Apply the control \a ctrl to the device + * \param ctrl The control to apply to the device + * \return 0 on success, a negative error code otherwise + */ +int V4L2Base::setControl(V4L2Control *ctrl) +{ + std::vector v = { ctrl, }; + return setControls(v); +} + /** * \var V4L2Base::fd_ * \brief The V4L2 device or subdevice device node file descriptor