From patchwork Thu Aug 8 15:12:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1752 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 30B46615FF for ; Thu, 8 Aug 2019 17:12:27 +0200 (CEST) Received: from localhost.localdomain (cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 90F76B7D; Thu, 8 Aug 2019 17:12:26 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1565277146; bh=HE+2iIf2jmH9xq9Ah+w8FOfgMN3VSOatszIlY4aDOxI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BVMYsW5XvrSEA1zhfcTSzMPKrRdvR7ifqiFilymo2xIrm7IWeF0cHJLKdvNCNMWMQ e+kBm/kMDeJvPYePAUd9Czja86IxgqtjF/VjaaoE29QJwyw+rhYmlqBkAR929ogPwe PX8uX+oJMCExJdcXa43P/NSlUBnHuHXjiNqv7pFg= From: Kieran Bingham To: LibCamera Devel Date: Thu, 8 Aug 2019 16:12:18 +0100 Message-Id: <20190808151221.24254-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190808151221.24254-1-kieran.bingham@ideasonboard.com> References: <20190808151221.24254-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/6] libcamera: v4l2_videodevice: Support M2M devices 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: Thu, 08 Aug 2019 15:12:27 -0000 V4L2 M2M devices represent a V4L2Device with two queues. One output, and one capture on a single device node. Represent this by instantiating a V4L2VideoDevice for each queue type, and preparing each device for its queue. Signed-off-by: Kieran Bingham --- src/libcamera/include/v4l2_videodevice.h | 26 ++- src/libcamera/v4l2_videodevice.cpp | 200 ++++++++++++++++++++--- 2 files changed, 201 insertions(+), 25 deletions(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index f5c8da93fcb5..24c58d787fde 100644 --- a/src/libcamera/include/v4l2_videodevice.h +++ b/src/libcamera/include/v4l2_videodevice.h @@ -71,6 +71,11 @@ struct V4L2Capability final : v4l2_capability { V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE); } + bool isM2M() const + { + return device_caps() & (V4L2_CAP_VIDEO_M2M | + V4L2_CAP_VIDEO_M2M_MPLANE); + } bool isMeta() const { return device_caps() & (V4L2_CAP_META_CAPTURE | @@ -123,7 +128,7 @@ public: V4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete; - int open(); + int open(int fd = -1, enum v4l2_buf_type type = V4L2_BUF_TYPE_PRIVATE); void close(); const char *driverName() const { return caps_.driver(); } @@ -152,6 +157,9 @@ protected: std::string logPrefix() const; private: + int queryBufferType(); + int queryBufferType(enum v4l2_buf_type type); + int getFormatMeta(V4L2DeviceFormat *format); int setFormatMeta(V4L2DeviceFormat *format); @@ -182,6 +190,22 @@ private: EventNotifier *fdEvent_; }; +class V4L2M2MDevice +{ +public: + V4L2M2MDevice(const std::string &deviceNode); + ~V4L2M2MDevice(); + + V4L2VideoDevice *output() { return output_; } + V4L2VideoDevice *capture() { return capture_; } + unsigned int status() { return status_; } + +private: + V4L2VideoDevice *output_; + V4L2VideoDevice *capture_; + unsigned int status_; +}; + } /* namespace libcamera */ #endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */ diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 81098dd70190..4bd33af5f3de 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -89,6 +89,12 @@ LOG_DECLARE_CATEGORY(V4L2) * \return True if the video device can capture or output images */ +/** + * \fn V4L2Capability::isM2M() + * \brief Identify if the device is an M2M device + * \return True if the device can capture and output images using the M2M API + */ + /** * \fn V4L2Capability::isMeta() * \brief Identify if the video device captures or outputs image meta-data @@ -294,17 +300,80 @@ V4L2VideoDevice::~V4L2VideoDevice() close(); } +int V4L2VideoDevice::queryBufferType() +{ + if (caps_.isVideoCapture()) { + fdEvent_ = new EventNotifier(fd(), EventNotifier::Read); + bufferType_ = caps_.isMultiplanar() + ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE + : V4L2_BUF_TYPE_VIDEO_CAPTURE; + } else if (caps_.isVideoOutput()) { + fdEvent_ = new EventNotifier(fd(), EventNotifier::Write); + bufferType_ = caps_.isMultiplanar() + ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + : V4L2_BUF_TYPE_VIDEO_OUTPUT; + } else if (caps_.isMetaCapture()) { + fdEvent_ = new EventNotifier(fd(), EventNotifier::Read); + bufferType_ = V4L2_BUF_TYPE_META_CAPTURE; + } else if (caps_.isMetaOutput()) { + fdEvent_ = new EventNotifier(fd(), EventNotifier::Write); + bufferType_ = V4L2_BUF_TYPE_META_OUTPUT; + } else { + LOG(V4L2, Error) << "Device is not a supported type"; + return -EINVAL; + } + + return 0; +} + +int V4L2VideoDevice::queryBufferType(enum v4l2_buf_type type) +{ + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + fdEvent_ = new EventNotifier(fd(), EventNotifier::Write); + bufferType_ = caps_.isMultiplanar() + ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE + : V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fdEvent_ = new EventNotifier(fd(), EventNotifier::Read); + bufferType_ = caps_.isMultiplanar() + ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE + : V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + default: + LOG(V4L2, Error) << "Unsupported buffer type"; + return -EINVAL; + } + + return 0; +} + /** * \brief Open a V4L2 video device and query its capabilities + * + * \param[in] fd The file descriptor to set (optional) + * \param[in] type The device type to operate on (optional) + * + * Opens a device or sets the file descriptor if provided, and then queries the + * capabilities of the device and establishes the buffer types and device events + * accordingly. + * * \return 0 on success or a negative error code otherwise */ -int V4L2VideoDevice::open() +int V4L2VideoDevice::open(int fd, enum v4l2_buf_type type) { int ret; - ret = V4L2Device::open(O_RDWR | O_NONBLOCK); - if (ret < 0) - return ret; + if (fd != -1) { + ret = V4L2Device::setFd(fd); + if (ret < 0) + return ret; + } else { + ret = V4L2Device::open(O_RDWR | O_NONBLOCK); + if (ret < 0) + return ret; + } ret = ioctl(VIDIOC_QUERYCAP, &caps_); if (ret < 0) { @@ -324,26 +393,10 @@ int V4L2VideoDevice::open() * devices (POLLIN), and write notifications for OUTPUT video devices * (POLLOUT). */ - if (caps_.isVideoCapture()) { - fdEvent_ = new EventNotifier(fd(), EventNotifier::Read); - bufferType_ = caps_.isMultiplanar() - ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE - : V4L2_BUF_TYPE_VIDEO_CAPTURE; - } else if (caps_.isVideoOutput()) { - fdEvent_ = new EventNotifier(fd(), EventNotifier::Write); - bufferType_ = caps_.isMultiplanar() - ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE - : V4L2_BUF_TYPE_VIDEO_OUTPUT; - } else if (caps_.isMetaCapture()) { - fdEvent_ = new EventNotifier(fd(), EventNotifier::Read); - bufferType_ = V4L2_BUF_TYPE_META_CAPTURE; - } else if (caps_.isMetaOutput()) { - fdEvent_ = new EventNotifier(fd(), EventNotifier::Write); - bufferType_ = V4L2_BUF_TYPE_META_OUTPUT; - } else { - LOG(V4L2, Error) << "Device is not a supported type"; - return -EINVAL; - } + if (type != V4L2_BUF_TYPE_PRIVATE) + queryBufferType(type); + else + queryBufferType(); fdEvent_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); fdEvent_->setEnabled(false); @@ -1143,4 +1196,103 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media, return new V4L2VideoDevice(mediaEntity); } +/** + * \class V4L2M2MDevice + * \brief Memory2Memory device container + * + * The V4L2M2MDevice manages two V4L2Device instances on the same + * deviceNode which operate together using two queues to implement the V4L2 + * Memory to Memory API. + * + * V4L2M2MDevices are opened at the point they are created, and will return + * both a capture and an output device or neither in the event of failure. + * + * The two devices should be configured and started as normal V4L2Device + * instances. Closing either of the devices will require recreating a new + * V4L2M2MDevice. + * + * The two V4L2Device instances share a single device node which is opened at + * the point the V4L2M2MDevice is created. + */ + +/** + * \fn V4L2M2MDevice::output + * \return The output V4L2Device instance + */ + +/** + * \fn V4L2M2MDevice::capture + * \return The capture V4L2Device instance + */ + +/** + * \fn V4L2M2MDevice::status + * \return The construction status of the V4L2M2MDevice + */ + +/** + * \brief Create a new V4L2M2MDevice from the \a deviceNode + * + * The deviceNode will be opened and shared across two V4L2VideoDevice + * instances. One to control the output stream, and one to control the capture + * stream. + * + * After construction, the status() must be checked to validate the instance. + */ +V4L2M2MDevice::V4L2M2MDevice(const std::string &deviceNode) + : status_(0) +{ + int fd[2] = { -1, -1 }; + int ret; + + output_ = new V4L2VideoDevice(deviceNode); + capture_ = new V4L2VideoDevice(deviceNode); + + /* Both V4L2Devices will have the same deviceNode. */ + fd[0] = ::open(deviceNode.c_str(), O_RDWR | O_NONBLOCK); + if (fd[0] < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to open V4L2 M2M device: " << strerror(-ret); + goto err; + } + + fd[1] = dup(fd[0]); + if (fd[1] < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to duplicate M2M device: " << strerror(-ret); + + goto err; + } + + ret = output_->open(fd[0], V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (ret) + goto err; + + ret = capture_->open(fd[1], V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ret) + goto err; + + return; + +err: + delete capture_; + delete output_; + + capture_ = nullptr; + output_ = nullptr; + + status_ = ret; + + close(fd[1]); + close(fd[0]); +} + +V4L2M2MDevice::~V4L2M2MDevice() +{ + delete capture_; + delete output_; +} + } /* namespace libcamera */