[libcamera-devel] [PATCH 2/2] lib: Add V4L2 Device object

Kieran Bingham kieran.bingham at ideasonboard.com
Fri Dec 21 13:37:24 CET 2018


Provide a helper V4L2 device object capable of interacting with the
V4L2 Linux Kernel APIs.

A test suite is added at test/v4l2_device/ to utilise and validate the
code base.

Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
---
 src/libcamera/include/v4l2_device.h   |  36 +++++++
 src/libcamera/meson.build             |   2 +
 src/libcamera/v4l2_device.cpp         | 137 ++++++++++++++++++++++++++
 test/meson.build                      |   2 +
 test/v4l2_device/double_open.cpp      |  32 ++++++
 test/v4l2_device/meson.build          |  12 +++
 test/v4l2_device/v4l2_device_test.cpp |  36 +++++++
 test/v4l2_device/v4l2_device_test.h   |  31 ++++++
 8 files changed, 288 insertions(+)
 create mode 100644 src/libcamera/include/v4l2_device.h
 create mode 100644 src/libcamera/v4l2_device.cpp
 create mode 100644 test/v4l2_device/double_open.cpp
 create mode 100644 test/v4l2_device/meson.build
 create mode 100644 test/v4l2_device/v4l2_device_test.cpp
 create mode 100644 test/v4l2_device/v4l2_device_test.h

diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
new file mode 100644
index 000000000000..619d932d3c82
--- /dev/null
+++ b/src/libcamera/include/v4l2_device.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * v4l2_device.h - V4L2 Device API Abstractions
+ */
+#ifndef __LIBCAMERA_V4L2_DEVICE_H__
+#define __LIBCAMERA_V4L2_DEVICE_H__
+
+#include <string>
+
+#include <linux/videodev2.h>
+
+namespace libcamera {
+
+class V4L2Device
+{
+public:
+	V4L2Device(const std::string &);
+	~V4L2Device();
+
+	bool isOpen();
+	int open();
+	void close();
+
+	int queryCap();
+
+private:
+	std::string device_;
+	int fd_;
+	int capabilities_;
+};
+
+}
+
+#endif /* __LIBCAMERA_V4L2_DEVICE_H__ */
diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
index f632eb5dd779..bbaf9a05ec2c 100644
--- a/src/libcamera/meson.build
+++ b/src/libcamera/meson.build
@@ -1,11 +1,13 @@
 libcamera_sources = files([
     'log.cpp',
     'main.cpp',
+    'v4l2_device.cpp',
 ])
 
 libcamera_headers = files([
     'include/log.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..eea2514f343c
--- /dev/null
+++ b/src/libcamera/v4l2_device.cpp
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * v4l2_device.cpp - V4L2 Device API
+ */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "log.h"
+#include "v4l2_device.h"
+
+/**
+ * \file v4l2_device.cpp
+ * \brief V4L2 Device API
+ */
+namespace libcamera {
+
+/**
+ * \class V4L2Device
+ * \brief V4L2Device object and API.
+ *
+ * The V4L2 Device API class models a single instance of a v4l2 device node.
+ */
+
+/**
+ * \fn V4L2Device::V4L2Device(device)
+ *
+ * Default constructor for a V4L2Device object. The \a device specifies a
+ * string to representation of the device object.
+ */
+V4L2Device::V4L2Device(const std::string &device)
+{
+	device_ = device;
+	fd_ = -1;
+	capabilities_ = 0;
+
+	LOG(Debug) << "V4L2Device Constructed for " << device_;
+}
+
+V4L2Device::~V4L2Device()
+{
+	close();
+
+	LOG(Debug) << "V4L2Device Destroyed";
+}
+
+/**
+ * \fn V4L2Device::isOpen(())
+ *
+ * Checks to see if we have successfully opened a v4l2 video device.
+ */
+bool V4L2Device::isOpen()
+{
+	return (fd_ != -1);
+}
+
+/**
+ * \fn V4L2Device::open()
+ *
+ * Opens a v4l2 device and queries properties from the device.
+ */
+int V4L2Device::open()
+{
+	int ret;
+
+	if (isOpen()) {
+		LOG(Error) << "Device already open";
+		return -EBADF;
+	}
+
+	fd_ = ::open(device_.c_str(), O_RDWR);
+	if (fd_ < 0) {
+		LOG(Error) << "Failed to open V4L2 device " << device_
+			   << " : " << strerror(errno);
+		return -errno;
+	}
+
+	ret = queryCap();
+	if (ret)
+		return ret;
+
+	if (!(capabilities_ & V4L2_CAP_STREAMING)) {
+		LOG(Error) << "Device does not support streaming IO";
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * \fn V4L2Device::close()
+ *
+ * Close a v4l2 device handle.
+ */
+void V4L2Device::close()
+{
+	if (fd_ < 0)
+		return;
+
+	::close(fd_);
+	fd_ = -1;
+}
+
+/**
+ * \fn V4L2Device::queryCap()
+ *
+ * Obtains the capabilities information from the V4L2 Device.
+ */
+int V4L2Device::queryCap()
+{
+	struct v4l2_capability cap = {};
+	int ret;
+
+	if (!isOpen()) {
+		LOG(Error) << "Attempt to query unopened device";
+		return -EBADF;
+	}
+
+	ret = ioctl(fd_, VIDIOC_QUERYCAP, &cap);
+	if (ret < 0) {
+		LOG(Error) << "Failed to query device capabilities";
+		return -1;
+	}
+
+	capabilities_ = cap.capabilities & V4L2_CAP_DEVICE_CAPS
+		      ? cap.device_caps : cap.capabilities;
+
+	return 0;
+}
+
+} /* namespace libcamera */
diff --git a/test/meson.build b/test/meson.build
index 754527324c7d..2bc76c289eea 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -12,6 +12,8 @@ test_includes_internal = [
   libcamera_internal_includes,
 ]
 
+subdir('v4l2_device')
+
 public_tests = [
   [ 'test_init',      'init.cpp' ],
 ]
diff --git a/test/v4l2_device/double_open.cpp b/test/v4l2_device/double_open.cpp
new file mode 100644
index 000000000000..1b7c7bfe14b8
--- /dev/null
+++ b/test/v4l2_device/double_open.cpp
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * libcamera V4L2 API tests
+ */
+
+#include "v4l2_device_test.h"
+
+class DoubleOpen : public V4L2DeviceTest
+{
+protected:
+	int run()
+	{
+		int ret;
+
+		/*
+		 * Expect failure: The device has already been opened by the
+		 * V4L2DeviceTest base class
+		 */
+		ret = dev->open();
+		if (!ret) {
+			fprintf(stderr, "Double open erroneously succeeded\n");
+			dev->close();
+			return TEST_FAIL;
+		}
+
+		return TEST_PASS;
+	}
+};
+
+TEST_REGISTER(DoubleOpen);
diff --git a/test/v4l2_device/meson.build b/test/v4l2_device/meson.build
new file mode 100644
index 000000000000..41675a303498
--- /dev/null
+++ b/test/v4l2_device/meson.build
@@ -0,0 +1,12 @@
+# Tests are listed in order of complexity.
+# They are not alphabetically sorted.
+v4l2_device_tests = [
+  [ 'double_open',        'double_open.cpp' ],
+]
+
+foreach t : v4l2_device_tests
+  exe = executable(t[0], [t[1], 'v4l2_device_test.cpp'],
+		   link_with : test_libraries,
+		   include_directories : test_includes_internal)
+  test(t[0], exe, suite: 'v4l2_device', is_parallel: false)
+endforeach
diff --git a/test/v4l2_device/v4l2_device_test.cpp b/test/v4l2_device/v4l2_device_test.cpp
new file mode 100644
index 000000000000..ae317a9519c5
--- /dev/null
+++ b/test/v4l2_device/v4l2_device_test.cpp
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * libcamera V4L2 API tests
+ */
+
+#include "v4l2_device_test.h"
+
+using namespace libcamera;
+
+V4L2DeviceTest::V4L2DeviceTest()
+{
+	dev = nullptr;
+}
+
+int V4L2DeviceTest::init()
+{
+	const char *device = "/dev/video0";
+
+	/* Validate the device node exists. */
+	if (!path_exists(device))
+		return TEST_SKIP;
+
+	dev = new V4L2Device(device);
+	if (!dev)
+		return TEST_FAIL;
+
+	return dev->open();
+}
+
+void V4L2DeviceTest::cleanup()
+{
+	if (dev)
+		delete dev;
+};
diff --git a/test/v4l2_device/v4l2_device_test.h b/test/v4l2_device/v4l2_device_test.h
new file mode 100644
index 000000000000..4374ddc7e932
--- /dev/null
+++ b/test/v4l2_device/v4l2_device_test.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2018, Google Inc.
+ *
+ * vl42device_test.h - libcamera v4l2device test base class
+ */
+
+#include "test.h"
+#include "v4l2_device.h"
+
+#ifndef __LIBCAMERA_V4L2_DEVICE_TEST_H_
+#define __LIBCAMERA_V4L2_DEVICE_TEST_H_
+
+using namespace libcamera;
+
+class V4L2DeviceTest : public Test
+{
+public:
+	V4L2DeviceTest();
+	virtual ~V4L2DeviceTest() {};
+
+protected:
+	int init();
+	void cleanup();
+
+	virtual int run() = 0;
+
+	V4L2Device *dev;
+};
+
+#endif /* __LIBCAMERA_V4L2_DEVICE_TEST_H_ */
-- 
2.17.1



More information about the libcamera-devel mailing list