From patchwork Wed Jun 19 11:08:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 1476 Return-Path: Received: from relay9-d.mail.gandi.net (relay9-d.mail.gandi.net [217.70.183.199]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id F0BBC61A3D for ; Wed, 19 Jun 2019 13:07:50 +0200 (CEST) X-Originating-IP: 2.224.242.101 Received: from uno.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101]) (Authenticated sender: jacopo@jmondi.org) by relay9-d.mail.gandi.net (Postfix) with ESMTPSA id 82E13FF803; Wed, 19 Jun 2019 11:07:50 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Wed, 19 Jun 2019 13:08:55 +0200 Message-Id: <20190619110858.20980-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190619110858.20980-1-jacopo@jmondi.org> References: <20190619110858.20980-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/6] libcamera: v4l2_device: Implement get and set controls 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: Wed, 19 Jun 2019 11:07:51 -0000 Implement getControls() and setControls() operations in V4L2Device class. Both operations take a V4L2Controls instance and read or write the V4L2 controls on the V4L2 device. Signed-off-by: Jacopo Mondi --- src/libcamera/include/v4l2_device.h | 6 + src/libcamera/v4l2_device.cpp | 189 ++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 563be151c257..e66cc7ebe737 100644 --- a/src/libcamera/include/v4l2_device.h +++ b/src/libcamera/include/v4l2_device.h @@ -15,6 +15,7 @@ namespace libcamera { class V4L2ControlInfo; +class V4L2Controls; class V4L2Device : protected Loggable { @@ -23,6 +24,10 @@ public: bool isOpen() const { return fd_ != -1; } const std::string &deviceNode() const { return deviceNode_; } + V4L2ControlInfo *getControlInfo(unsigned int id); + int getControls(V4L2Controls *ctrls); + int setControls(V4L2Controls *ctrls); + protected: V4L2Device(const std::string &deviceNode, const std::string &logTag); ~V4L2Device(); @@ -37,6 +42,7 @@ protected: private: void listControls(); + int validateControlType(V4L2ControlInfo *info); std::map controls_; std::string deviceNode_; diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp index 58dd94836686..5af0ddc468fe 100644 --- a/src/libcamera/v4l2_device.cpp +++ b/src/libcamera/v4l2_device.cpp @@ -66,6 +66,170 @@ void V4L2Device::close() * \return The device node path */ +/** + * \brief Get informations for control with \a id + * \param[in] id The control id to search info for + * \return The V4L2ControlInfo associated to the V4L2 control with \a id or + * nullptr if the control is not supported by the V4L2Device + */ +V4L2ControlInfo *V4L2Device::getControlInfo(unsigned int id) +{ + auto it = controls_.find(id); + if (it == controls_.end()) + return nullptr; + + return it->second; +} + +/** + * \brief Read values of a list of controls from the device + * \param[inout] ctrls The list of controls to read + * + * Read the value of each V4L2Controls contained in \a ctrls, overwriting + * it's current value with the one returned by the device. + * + * Each V4L2Control instance in \a ctrls should be supported by the device. + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::getControls(V4L2Controls *ctrls) +{ + unsigned int count = ctrls->size(); + if (count == 0) + return 0; + + struct V4L2ControlInfo *controlInfo[count]; + struct v4l2_ext_control v4l2Ctrls[count]; + for (unsigned int i = 0; i < count; ++i) { + V4L2Control *ctrl = (*ctrls)[i]; + + /* Validate the control. */ + V4L2ControlInfo *info = getControlInfo(ctrl->id()); + if (!info) { + LOG(V4L2, Error) << "Control '" << ctrl->id() + << "' not valid"; + return -EINVAL; + } + + if (validateControlType(info)) + return -EINVAL; + + controlInfo[i] = info; + + /* Prepare the v4l2_ext_control entry for the read operation. */ + struct v4l2_ext_control *v4l2Ctrl = &v4l2Ctrls[i]; + v4l2Ctrl->id = info->id(); + v4l2Ctrl->size = info->size(); + } + + struct v4l2_ext_controls v4l2ExtCtrls = {}; + v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL; + v4l2ExtCtrls.controls = v4l2Ctrls; + v4l2ExtCtrls.count = count; + + int ret = ioctl(VIDIOC_G_EXT_CTRLS, &v4l2ExtCtrls); + if (ret) { + LOG(V4L2, Error) << "Unable to read controls: " + << strerror(ret); + return ret; + } + + /* + * For each control read from the device, set the value in the + * V4L2ControlValue provided by the caller. + */ + for (unsigned int i = 0; i < count; ++i) { + struct v4l2_ext_control *v4l2Ctrl = &v4l2Ctrls[i]; + V4L2ControlInfo *info = controlInfo[i]; + V4L2Control *ctrl = (*ctrls)[i]; + + switch (info->type()) { + case V4L2_CTRL_TYPE_INTEGER64: + ctrl->setValue(v4l2Ctrl->value64); + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_MENU: + ctrl->setValue(static_cast(v4l2Ctrl->value)); + break; + } + } + + return 0; +} + +/** + * \brief Write a list of controls to the device + * \param[in] ctrls The list of controls to apply + * + * Write the value of each V4L2Control contained in \a ctrls. Each + * control should be initialized by the caller with a value, otherwise + * the result of the operation is undefined. + * + * The value of each control is not updated to reflect what has been + * actually applied on the device. To read the actual value of a control + * after a setControls(), users should read the control value back with + * getControls(). + * + * Each V4L2Control instance in \a ctrls should be supported by the device. + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2Device::setControls(V4L2Controls *ctrls) +{ + unsigned int count = ctrls->size(); + if (count == 0) + return 0; + + struct v4l2_ext_control v4l2Ctrls[count]; + for (unsigned int i = 0; i < count; ++i) { + V4L2Control *ctrl = (*ctrls)[i]; + + /* Validate the control. */ + V4L2ControlInfo *info = getControlInfo(ctrl->id()); + if (!info) { + LOG(V4L2, Error) << "Control '" << ctrl->id() + << "' not valid"; + return -EINVAL; + } + + if (validateControlType(info)) + return -EINVAL; + + /* Prepare the v4l2_ext_control entry for the write operation. */ + struct v4l2_ext_control *v4l2Ctrl = &v4l2Ctrls[i]; + v4l2Ctrl->id = info->id(); + v4l2Ctrl->size = info->size(); + + switch (info->type()) { + case V4L2_CTRL_TYPE_INTEGER64: + v4l2Ctrl->value64 = ctrl->value(); + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_MENU: + v4l2Ctrl->value = static_cast(ctrl->value()); + break; + } + } + + struct v4l2_ext_controls v4l2ExtCtrls = {}; + v4l2ExtCtrls.which = V4L2_CTRL_WHICH_CUR_VAL; + v4l2ExtCtrls.controls = v4l2Ctrls; + v4l2ExtCtrls.count = count; + + int ret = ioctl(VIDIOC_S_EXT_CTRLS, &v4l2ExtCtrls); + if (ret) { + LOG(V4L2, Error) << "Unable to write controls: " + << strerror(ret); + return ret; + } + + return 0; +} + /** * \brief Construct a V4L2Device * \param[in] deviceNode The device node filesystem path @@ -171,4 +335,29 @@ void V4L2Device::listControls() } } +/* + * \brief Make sure the control type is supported + * \param[in] info The V4L2ControlInfo to inspect type of + * \return 0 on success or a negative error code otherwise + * \retval -EINVAL The control type is not supported + */ +int V4L2Device::validateControlType(V4L2ControlInfo *info) +{ + /* \todo support compound and menu controls. */ + switch (info->type()) { + case V4L2_CTRL_TYPE_INTEGER64: + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_MENU: + break; + default: + LOG(V4L2, Error) << "Control type '" << info->type() + << "' not supported"; + return -EINVAL; + } + + return 0; +} + } /* namespace libcamera */