[libcamera-devel,v4,1/2] libcamera: Add V4L2 Device object

Message ID 20190117212703.24047-2-kieran.bingham@ideasonboard.com
State Accepted
Commit e74f3eebb498634f438a005b2ad5b7f1778091e8
Headers show
Series
  • V4L2Device: Add basic V4L2 support class
Related show

Commit Message

Kieran Bingham Jan. 17, 2019, 9:27 p.m. UTC
Provide a helper V4L2 device object capable of interacting with the
V4L2 Linux Kernel APIs.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 src/libcamera/include/v4l2_device.h |  60 +++++++++
 src/libcamera/meson.build           |   2 +
 src/libcamera/v4l2_device.cpp       | 186 ++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 src/libcamera/include/v4l2_device.h
 create mode 100644 src/libcamera/v4l2_device.cpp

Comments

Niklas Söderlund Jan. 17, 2019, 9:55 p.m. UTC | #1
Hi Kieran,

Thanks for your persistence with this series :-)

On 2019-01-17 21:27:02 +0000, Kieran Bingham wrote:
> Provide a helper V4L2 device object capable of interacting with the
> V4L2 Linux Kernel APIs.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

> ---
>  src/libcamera/include/v4l2_device.h |  60 +++++++++
>  src/libcamera/meson.build           |   2 +
>  src/libcamera/v4l2_device.cpp       | 186 ++++++++++++++++++++++++++++
>  3 files changed, 248 insertions(+)
>  create mode 100644 src/libcamera/include/v4l2_device.h
>  create mode 100644 src/libcamera/v4l2_device.cpp
> 
> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> new file mode 100644
> index 000000000000..474c05b58cef
> --- /dev/null
> +++ b/src/libcamera/include/v4l2_device.h
> @@ -0,0 +1,60 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * v4l2_device.h - V4L2 Device
> + */
> +#ifndef __LIBCAMERA_V4L2_DEVICE_H__
> +#define __LIBCAMERA_V4L2_DEVICE_H__
> +
> +#include <string>
> +
> +#include <linux/videodev2.h>
> +
> +namespace libcamera {
> +
> +struct V4L2Capability final : v4l2_capability {
> +	const char *driver() const
> +	{
> +		return reinterpret_cast<const char *>(v4l2_capability::driver);
> +	}
> +	const char *card() const
> +	{
> +		return reinterpret_cast<const char *>(v4l2_capability::card);
> +	}
> +	const char *bus_info() const
> +	{
> +		return reinterpret_cast<const char *>(v4l2_capability::bus_info);
> +	}
> +
> +	bool isCapture() const { return capabilities & V4L2_CAP_VIDEO_CAPTURE; }
> +	bool isOutput() const { return capabilities & V4L2_CAP_VIDEO_OUTPUT; }
> +	bool hasStreaming() const { return capabilities & V4L2_CAP_STREAMING; }
> +};
> +
> +class V4L2Device
> +{
> +public:
> +	V4L2Device(const std::string &devnode);
> +	V4L2Device(const V4L2Device &) = delete;
> +	~V4L2Device();
> +
> +	void operator=(const V4L2Device &) = delete;
> +
> +	int open();
> +	bool isOpen() const;
> +	void close();
> +
> +	const char *driverName() const { return caps_.driver(); }
> +	const char *deviceName() const { return caps_.card(); }
> +	const char *busName() const { return caps_.bus_info(); }
> +
> +private:
> +	std::string devnode_;
> +	int fd_;
> +	V4L2Capability caps_;
> +};
> +
> +} /* namespace libcamera */
> +
> +#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */
> diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> index abf9a71d4172..f9f25c0ecf15 100644
> --- a/src/libcamera/meson.build
> +++ b/src/libcamera/meson.build
> @@ -11,6 +11,7 @@ libcamera_sources = files([
>      'pipeline_handler.cpp',
>      'signal.cpp',
>      'timer.cpp',
> +    'v4l2_device.cpp',
>  ])
>  
>  libcamera_headers = files([
> @@ -21,6 +22,7 @@ libcamera_headers = files([
>      'include/media_object.h',
>      'include/pipeline_handler.h',
>      'include/utils.h',
> +    'include/v4l2_device.h',
>  ])
>  
>  libcamera_internal_includes =  include_directories('include')
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> new file mode 100644
> index 000000000000..54b53de37510
> --- /dev/null
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -0,0 +1,186 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2019, Google Inc.
> + *
> + * v4l2_device.cpp - V4L2 Device
> + */
> +
> +#include <fcntl.h>
> +#include <string.h>
> +#include <sys/ioctl.h>
> +#include <sys/mman.h>
> +#include <unistd.h>
> +
> +#include "log.h"
> +#include "v4l2_device.h"
> +
> +/**
> + * \file v4l2_device.h
> + * \brief V4L2 Device API
> + */
> +namespace libcamera {
> +
> +/**
> + * \struct V4L2Capability
> + * \brief struct v4l2_capability object wrapper and helpers
> + *
> + * The V4L2Capability structure manages the information returned by the
> + * VIDIOC_QUERYCAP ioctl.
> + */
> +
> +/**
> + * \fn const char *V4L2Capability::driver()
> + * \brief Retrieve the driver module name
> + * \return The string containing the name of the driver module
> + */
> +
> +/**
> + * \fn const char *V4L2Capability::card()
> + * \brief Retrieve the device card name
> + * \return The string containing the device name
> + */
> +
> +/**
> + * \fn const char *V4L2Capability::bus_info()
> + * \brief Retrieve the location of the device in the system
> + * \return The string containing the device location
> + */
> +
> +/**
> + * \fn bool V4L2Capability::isCapture()
> + * \brief Identify if the device is capable of capturing video
> + * \return True if the device can capture video frames
> + */
> +
> +/**
> + * \fn bool V4L2Capability::isOutput()
> + * \brief Identify if the device is capable of outputting video
> + * \return True if the device can output video frames
> + */
> +
> +/**
> + * \fn bool V4L2Capability::hasStreaming()
> + * \brief Determine if the device can perform Streaming I/O
> + * \return True if the device provides Streaming I/O IOCTLs
> + */
> +
> +/**
> + * \class V4L2Device
> + * \brief V4L2Device object and API
> + *
> + * The V4L2 Device API class models an instance of a V4L2 device node.
> + * It is constructed with the path to a V4L2 video device node. The device node
> + * is only opened upon a call to open() which must be checked for success.
> + *
> + * The device capabilities are validated when the device is opened and the
> + * device is rejected if it is not a suitable V4L2 capture or output device, or
> + * if the device does not support streaming I/O.
> + *
> + * No API call other than open(), isOpen() and close() shall be called on an
> + * unopened device instance.
> + *
> + * Upon destruction any device left open will be closed, and any resources
> + * released.
> + */
> +
> +/**
> + * \brief Construct a V4L2Device
> + * \param devnode The file-system path to the video device node
> + */
> +V4L2Device::V4L2Device(const std::string &devnode)
> +	: devnode_(devnode), fd_(-1)
> +{
> +}
> +
> +V4L2Device::~V4L2Device()
> +{
> +	close();
> +}
> +
> +/**
> + * \brief Open a V4L2 device and query its capabilities
> + * \return 0 on success, or a negative error code otherwise
> + */
> +int V4L2Device::open()
> +{
> +	int ret;
> +
> +	if (isOpen()) {
> +		LOG(Error) << "Device already open";
> +		return -EBUSY;
> +	}
> +
> +	ret = ::open(devnode_.c_str(), O_RDWR);
> +	if (ret < 0) {
> +		ret = -errno;
> +		LOG(Error) << "Failed to open V4L2 device '" << devnode_
> +			   << "': " << strerror(-ret);
> +		return ret;
> +	}
> +	fd_ = ret;
> +
> +	ret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);
> +	if (ret < 0) {
> +		ret = -errno;
> +		LOG(Error) << "Failed to query device capabilities: "
> +			   << strerror(-ret);
> +		return ret;
> +	}
> +
> +	LOG(Debug) << "Opened '" << devnode_ << "' "
> +		   << caps_.bus_info() << ": " << caps_.driver()
> +		   << ": " << caps_.card();
> +
> +	if (!caps_.isCapture() && !caps_.isOutput()) {
> +		LOG(Debug) << "Device is not a supported type";
> +		return -EINVAL;
> +	}
> +
> +	if (!caps_.hasStreaming()) {
> +		LOG(Error) << "Device does not support streaming I/O";
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * \brief Check if device is successfully opened
> + * \return True if the device is open, false otherwise
> + */
> +bool V4L2Device::isOpen() const
> +{
> +	return fd_ != -1;
> +}
> +
> +/**
> + * \brief Close the device, releasing any resources acquired by open()
> + */
> +void V4L2Device::close()
> +{
> +	if (fd_ < 0)
> +		return;
> +
> +	::close(fd_);
> +	fd_ = -1;
> +}
> +
> +/**
> + * \fn const char *V4L2Device::driverName()
> + * \brief Retrieve the name of the V4L2 device driver
> + * \return The string containing the driver name
> + */
> +
> +/**
> + * \fn const char *V4L2Device::deviceName()
> + * \brief Retrieve the name of the V4L2 device
> + * \return The string containing the device name
> + */
> +
> +/**
> + * \fn const char *V4L2Device::busName()
> + * \brief Retrieve the location of the device in the system
> + * \return The string containing the device location
> + */
> +
> +} /* namespace libcamera */
> -- 
> 2.17.1
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
new file mode 100644
index 000000000000..474c05b58cef
--- /dev/null
+++ b/src/libcamera/include/v4l2_device.h
@@ -0,0 +1,60 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * v4l2_device.h - V4L2 Device
+ */
+#ifndef __LIBCAMERA_V4L2_DEVICE_H__
+#define __LIBCAMERA_V4L2_DEVICE_H__
+
+#include <string>
+
+#include <linux/videodev2.h>
+
+namespace libcamera {
+
+struct V4L2Capability final : v4l2_capability {
+	const char *driver() const
+	{
+		return reinterpret_cast<const char *>(v4l2_capability::driver);
+	}
+	const char *card() const
+	{
+		return reinterpret_cast<const char *>(v4l2_capability::card);
+	}
+	const char *bus_info() const
+	{
+		return reinterpret_cast<const char *>(v4l2_capability::bus_info);
+	}
+
+	bool isCapture() const { return capabilities & V4L2_CAP_VIDEO_CAPTURE; }
+	bool isOutput() const { return capabilities & V4L2_CAP_VIDEO_OUTPUT; }
+	bool hasStreaming() const { return capabilities & V4L2_CAP_STREAMING; }
+};
+
+class V4L2Device
+{
+public:
+	V4L2Device(const std::string &devnode);
+	V4L2Device(const V4L2Device &) = delete;
+	~V4L2Device();
+
+	void operator=(const V4L2Device &) = delete;
+
+	int open();
+	bool isOpen() const;
+	void close();
+
+	const char *driverName() const { return caps_.driver(); }
+	const char *deviceName() const { return caps_.card(); }
+	const char *busName() const { return caps_.bus_info(); }
+
+private:
+	std::string devnode_;
+	int fd_;
+	V4L2Capability caps_;
+};
+
+} /* namespace libcamera */
+
+#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index abf9a71d4172..f9f25c0ecf15 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -11,6 +11,7 @@  libcamera_sources = files([
     'pipeline_handler.cpp',
     'signal.cpp',
     'timer.cpp',
+    'v4l2_device.cpp',
 ])
 
 libcamera_headers = files([
@@ -21,6 +22,7 @@  libcamera_headers = files([
     'include/media_object.h',
     'include/pipeline_handler.h',
     'include/utils.h',
+    'include/v4l2_device.h',
 ])
 
 libcamera_internal_includes =  include_directories('include')
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
new file mode 100644
index 000000000000..54b53de37510
--- /dev/null
+++ b/src/libcamera/v4l2_device.cpp
@@ -0,0 +1,186 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2019, Google Inc.
+ *
+ * v4l2_device.cpp - V4L2 Device
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "v4l2_device.h"
+
+/**
+ * \file v4l2_device.h
+ * \brief V4L2 Device API
+ */
+namespace libcamera {
+
+/**
+ * \struct V4L2Capability
+ * \brief struct v4l2_capability object wrapper and helpers
+ *
+ * The V4L2Capability structure manages the information returned by the
+ * VIDIOC_QUERYCAP ioctl.
+ */
+
+/**
+ * \fn const char *V4L2Capability::driver()
+ * \brief Retrieve the driver module name
+ * \return The string containing the name of the driver module
+ */
+
+/**
+ * \fn const char *V4L2Capability::card()
+ * \brief Retrieve the device card name
+ * \return The string containing the device name
+ */
+
+/**
+ * \fn const char *V4L2Capability::bus_info()
+ * \brief Retrieve the location of the device in the system
+ * \return The string containing the device location
+ */
+
+/**
+ * \fn bool V4L2Capability::isCapture()
+ * \brief Identify if the device is capable of capturing video
+ * \return True if the device can capture video frames
+ */
+
+/**
+ * \fn bool V4L2Capability::isOutput()
+ * \brief Identify if the device is capable of outputting video
+ * \return True if the device can output video frames
+ */
+
+/**
+ * \fn bool V4L2Capability::hasStreaming()
+ * \brief Determine if the device can perform Streaming I/O
+ * \return True if the device provides Streaming I/O IOCTLs
+ */
+
+/**
+ * \class V4L2Device
+ * \brief V4L2Device object and API
+ *
+ * The V4L2 Device API class models an instance of a V4L2 device node.
+ * It is constructed with the path to a V4L2 video device node. The device node
+ * is only opened upon a call to open() which must be checked for success.
+ *
+ * The device capabilities are validated when the device is opened and the
+ * device is rejected if it is not a suitable V4L2 capture or output device, or
+ * if the device does not support streaming I/O.
+ *
+ * No API call other than open(), isOpen() and close() shall be called on an
+ * unopened device instance.
+ *
+ * Upon destruction any device left open will be closed, and any resources
+ * released.
+ */
+
+/**
+ * \brief Construct a V4L2Device
+ * \param devnode The file-system path to the video device node
+ */
+V4L2Device::V4L2Device(const std::string &devnode)
+	: devnode_(devnode), fd_(-1)
+{
+}
+
+V4L2Device::~V4L2Device()
+{
+	close();
+}
+
+/**
+ * \brief Open a V4L2 device and query its capabilities
+ * \return 0 on success, or a negative error code otherwise
+ */
+int V4L2Device::open()
+{
+	int ret;
+
+	if (isOpen()) {
+		LOG(Error) << "Device already open";
+		return -EBUSY;
+	}
+
+	ret = ::open(devnode_.c_str(), O_RDWR);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(Error) << "Failed to open V4L2 device '" << devnode_
+			   << "': " << strerror(-ret);
+		return ret;
+	}
+	fd_ = ret;
+
+	ret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(Error) << "Failed to query device capabilities: "
+			   << strerror(-ret);
+		return ret;
+	}
+
+	LOG(Debug) << "Opened '" << devnode_ << "' "
+		   << caps_.bus_info() << ": " << caps_.driver()
+		   << ": " << caps_.card();
+
+	if (!caps_.isCapture() && !caps_.isOutput()) {
+		LOG(Debug) << "Device is not a supported type";
+		return -EINVAL;
+	}
+
+	if (!caps_.hasStreaming()) {
+		LOG(Error) << "Device does not support streaming I/O";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * \brief Check if device is successfully opened
+ * \return True if the device is open, false otherwise
+ */
+bool V4L2Device::isOpen() const
+{
+	return fd_ != -1;
+}
+
+/**
+ * \brief Close the device, releasing any resources acquired by open()
+ */
+void V4L2Device::close()
+{
+	if (fd_ < 0)
+		return;
+
+	::close(fd_);
+	fd_ = -1;
+}
+
+/**
+ * \fn const char *V4L2Device::driverName()
+ * \brief Retrieve the name of the V4L2 device driver
+ * \return The string containing the driver name
+ */
+
+/**
+ * \fn const char *V4L2Device::deviceName()
+ * \brief Retrieve the name of the V4L2 device
+ * \return The string containing the device name
+ */
+
+/**
+ * \fn const char *V4L2Device::busName()
+ * \brief Retrieve the location of the device in the system
+ * \return The string containing the device location
+ */
+
+} /* namespace libcamera */