From patchwork Tue Aug 13 09:40:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kieran Bingham X-Patchwork-Id: 1802 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 BFC3C61624 for ; Tue, 13 Aug 2019 11:40:25 +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 4E3DAFA0; Tue, 13 Aug 2019 11:40:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1565689225; bh=f/HUeRJSLJmSZu+rCLW2nAb7pY/qBSCbAOGt8l90Apk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vpU9opdQlpvjQ4dShsh1WU8iphDTItPDioUwCNAdFHXVKwsz6COQatEGq9b+4pu6w oIhYHu6lYg8AKgOv/vxZR9HakYiadWbTimsbs7CRIKTMK2U2w+r+7l3NgE1l4AC0xX mu3B3z6RsHu2gW2JkM4ZL9BtvbmOn4VdeJfhpTgQ= From: Kieran Bingham To: LibCamera Devel Date: Tue, 13 Aug 2019 10:40:17 +0100 Message-Id: <20190813094020.10277-4-kieran.bingham@ideasonboard.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190813094020.10277-1-kieran.bingham@ideasonboard.com> References: <20190813094020.10277-1-kieran.bingham@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v3 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: Tue, 13 Aug 2019 09:40:25 -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 Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- src/libcamera/include/v4l2_videodevice.h | 25 +++ src/libcamera/v4l2_videodevice.cpp | 194 ++++++++++++++++++++++- 2 files changed, 218 insertions(+), 1 deletion(-) diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h index f5c8da93fcb5..0fc2dcd81d2b 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 | @@ -124,6 +129,7 @@ public: V4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete; int open(); + int open(int handle, enum v4l2_buf_type type); void close(); const char *driverName() const { return caps_.driver(); } @@ -182,6 +188,25 @@ private: EventNotifier *fdEvent_; }; +class V4L2M2MDevice +{ +public: + V4L2M2MDevice(const std::string &deviceNode); + ~V4L2M2MDevice(); + + int open(); + void close(); + + V4L2VideoDevice *output() { return output_; } + V4L2VideoDevice *capture() { return capture_; } + +private: + std::string deviceNode_; + + V4L2VideoDevice *output_; + V4L2VideoDevice *capture_; +}; + } /* namespace libcamera */ #endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */ diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 81098dd70190..5e41f44eeb50 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 @@ -295,7 +301,8 @@ V4L2VideoDevice::~V4L2VideoDevice() } /** - * \brief Open a V4L2 video device and query its capabilities + * \brief Open the V4L2 video device node and query its capabilities + * * \return 0 on success or a negative error code otherwise */ int V4L2VideoDevice::open() @@ -355,6 +362,92 @@ int V4L2VideoDevice::open() return 0; } +/** + * \brief Open a V4L2 video device from an opened file handle and query its + * capabilities + * \param[in] handle The file descriptor to set + * \param[in] type The device type to operate on + * + * This methods opens a video device from the existing file descriptor \a + * handle. Like open(), this method queries the capabilities of the device, but + * handles it according to the given device \a type instead of determining its + * type from the capabilities. This can be used to force a given device type for + * M2M devices. + * + * The file descriptor \a handle is duplicated, and the caller is responsible + * for closing the \a handle when it has no further use for it. The close() + * method will close the duplicated file descriptor, leaving \a handle + * untouched. + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type) +{ + int ret; + int newFd; + + newFd = dup(handle); + if (newFd < 0) { + ret = -errno; + LOG(V4L2, Error) << "Failed to duplicate file handle: " + << strerror(-ret); + return ret; + } + + ret = V4L2Device::setFd(newFd); + if (ret < 0) { + LOG(V4L2, Error) << "Failed to set file handle: " + << strerror(-ret); + ::close(newFd); + return ret; + } + + ret = ioctl(VIDIOC_QUERYCAP, &caps_); + if (ret < 0) { + LOG(V4L2, Error) + << "Failed to query device capabilities: " + << strerror(-ret); + return ret; + } + + if (!caps_.hasStreaming()) { + LOG(V4L2, Error) << "Device does not support streaming I/O"; + return -EINVAL; + } + + /* + * Set buffer type and wait for read notifications on CAPTURE video + * devices (POLLIN), and write notifications for OUTPUT video devices + * (POLLOUT). + */ + 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; + } + + fdEvent_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); + fdEvent_->setEnabled(false); + + LOG(V4L2, Debug) + << "Opened device " << caps_.bus_info() << ": " + << caps_.driver() << ": " << caps_.card(); + + return 0; +} + /** * \brief Close the video device, releasing any resources acquired by open() */ @@ -1143,4 +1236,103 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media, return new V4L2VideoDevice(mediaEntity); } +/** + * \class V4L2M2MDevice + * \brief Memory2Memory video device + * + * The V4L2M2MDevice manages two V4L2VideoDevice instances on the same + * deviceNode which operate together using two queues to implement the V4L2 + * Memory to Memory API. + * + * The two devices should be opened by calling open() on the V4L2M2MDevice, and + * can be closed by calling close on the V4L2M2MDevice. + * + * Calling V4L2VideoDevice::open() and V4L2VideoDevice::close() on the capture + * or output V4L2VideoDevice is not permitted. + */ + +/** + * \fn V4L2M2MDevice::output + * \brief Retrieve the output V4L2VideoDevice instance + * \return The output V4L2Device instance + */ + +/** + * \fn V4L2M2MDevice::capture + * \brief Retrieve the capture V4L2VideoDevice instance + * \return The capture V4L2Device instance + */ + +/** + * \brief Create a new V4L2M2MDevice from the \a deviceNode + */ +V4L2M2MDevice::V4L2M2MDevice(const std::string &deviceNode) + : deviceNode_(deviceNode) +{ + output_ = new V4L2VideoDevice(deviceNode); + capture_ = new V4L2VideoDevice(deviceNode); +} + +V4L2M2MDevice::~V4L2M2MDevice() +{ + delete capture_; + delete output_; +} + +/** + * \brief Open a V4L2 Memory to Memory device + * + * Open the device node and prepare the two V4L2VideoDevice instances to handle + * their respective buffer queues. + * + * \return 0 on success or a negative error code otherwise + */ +int V4L2M2MDevice::open() +{ + int fd; + int ret; + + /* + * The output and capture V4L2VideoDevice instances use the same file + * handle for the same device node. The local file handle can be closed + * as the V4L2VideoDevice::open() retains a handle by duplicating the + * fd passed in. + */ + fd = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK); + if (fd < 0) { + ret = -errno; + LOG(V4L2, Error) + << "Failed to open V4L2 M2M device: " << strerror(-ret); + return ret; + } + + ret = output_->open(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (ret) + goto err; + + ret = capture_->open(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (ret) + goto err; + + ::close(fd); + + return 0; + +err: + close(); + ::close(fd); + + return ret; +} + +/** + * \brief Close the memory-to-memory device, releasing any resources acquired by + * open() + */ +void V4L2M2MDevice::close() +{ + capture_->close(); + output_->close(); +} + } /* namespace libcamera */