summaryrefslogtreecommitdiff
path: root/src/v4l2/v4l2_compat.cpp
AgeCommit message (Collapse)Author
2020-06-19v4l2: v4l2_compat: Intercept open64, openat64, and mmap64Paul Elder
Some applications (eg. Firefox, Google Chrome, Skype) use open64, openat64, and mmap64 instead of their non-64 versions that we currently intercept. Intercept these calls as well. _LARGEFILE64_SOURCE needs to be set so that the 64-bit symbols are available and not synonymous to the non-64-bit versions on 64-bit systems. Also, since we set _FILE_OFFSET_BITS to 32 to force the various open and mmap symbols that we export to not be the 64-bit versions, our dlsym to get the original open and mmap calls will not automatically be converted to their 64-bit versions. Since we intercept both 32-bit and 64-bit versions of open and mmap, we should be using the 64-bit version to service both. Fetch the 64-bit versions of openat and mmap directly. musl defines the 64-bit symbols as macros that are equivalent to the non-64-bit symbols, so we put compile guards that check if the 64-bit symbols are defined. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Tested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> # Compile with musl Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2020-05-28v4l2: Relicense V4L2 compatibility layer under LGPLLaurent Pinchart
The V4L2 compatibility layer is licensed under the GPL. It is compiled as a binary separate from libcamera.so, and is loaded into the address space of processes through LD_PRELOAD to intercept calls to the C library. It is our understanding and intent that the GPL license doesn't propagate to the binaries whose calls are intercepted, considering those binaries are not derivative work of the V4L2 compatibility layer and are not designed to be linked to the V4L2 compatibility layer. There is however a possibly grey area if binaries are packaged with a shell script wrapper that loads the V4L2 compatibility layer. This could lead to license-related issues if such packaging is performed by Linux distributions or system integrators. To clarify the intent and lift the doubts, relicense the V4L2 compatibility layer under the LGPL. The V4L2 compatibility layer code itself still benefits from the license protection, while its usage with third-party binaries is clearly allowed as intended. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Acked-by: Jacopo Mondi <jacopo@jmondi.org> Acked-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Acked-by: Paul Elder <paul.elder@ideasonboard.com> Acked-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Acked-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2020-01-04v4l2: Fix compilation of __open_2() and __openat_2() with gccLaurent Pinchart
The __open_2() and __openat_2() functions are defined by glibc as taking 2 and 3 arguments respectively, with variadic arguments for the file mode as open() and openat() do. The V4L2 compatibility layer defines them as aliases for open() and openat(), which results in compilation failures with gcc: ../../src/v4l2/v4l2_compat.cpp: In function ‘int __openat_2(int, const char*, int)’: ../../src/v4l2/v4l2_compat.cpp:58:14: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive] 58 | return open(dirfd, path, oflag); | ^~~~~ | | | int ../../src/v4l2/v4l2_compat.cpp:31:39: note: initializing argument 1 of ‘int open(const char*, int, ...)’ 31 | LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...) | ~~~~~~~~~~~~^~~~ ../../src/v4l2/v4l2_compat.cpp:58:21: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive] 58 | return open(dirfd, path, oflag); | ^~~~ | | | const char* ../../src/v4l2/v4l2_compat.cpp:31:49: note: initializing argument 2 of ‘int open(const char*, int, ...)’ 31 | LIBCAMERA_PUBLIC int open(const char *path, int oflag, ...) | Fix this by defining the two functions as wrappers around open() and openat() without variadic arguments. Fixes: 0ce8f2390b52 ("v4l2: v4l2_compat: Add V4L2 compatibility layer") Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-01-03v4l2: v4l2_compat: Add V4L2 compatibility layerPaul Elder
Add libcamera V4L2 compatibility layer. This initial implementation supports the minimal set of V4L2 operations, which allows getting, setting, and enumerating formats, and streaming frames from a video device. Some data about the wrapped V4L2 video device are hardcoded. Add a build option named 'v4l2' and adjust the build system to selectively compile the V4L2 compatibility layer. For now we match the V4L2 device node to a libcamera camera based on a devnum that a pipeline handler may optionally map to a libcamera camera. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
'>211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * main.cpp - cam - The libcamera swiss army knife
 */

#include <atomic>
#include <iomanip>
#include <iostream>
#include <signal.h>
#include <string.h>

#include <libcamera/libcamera.h>
#include <libcamera/property_ids.h>

#include "camera_session.h"
#include "event_loop.h"
#include "main.h"
#include "options.h"
#include "stream_options.h"

using namespace libcamera;

class CamApp
{
public:
	CamApp();

	static CamApp *instance();

	int init(int argc, char **argv);
	void cleanup();

	int exec();
	void quit();

private:
	void cameraAdded(std::shared_ptr<Camera> cam);
	void cameraRemoved(std::shared_ptr<Camera> cam);
	void captureDone();
	int parseOptions(int argc, char *argv[]);
	int run();

	static std::string cameraName(const Camera *camera);

	static CamApp *app_;
	OptionsParser::Options options_;

	std::unique_ptr<CameraManager> cm_;

	std::atomic_uint loopUsers_;
	EventLoop loop_;
};

CamApp *CamApp::app_ = nullptr;

CamApp::CamApp()
	: loopUsers_(0)
{
	CamApp::app_ = this;
}

CamApp *CamApp::instance()
{
	return CamApp::app_;
}

int CamApp::init(int argc, char **argv)
{
	int ret;

	ret = parseOptions(argc, argv);
	if (ret < 0)
		return ret;

	cm_ = std::make_unique<CameraManager>();

	ret = cm_->start();
	if (ret) {
		std::cout << "Failed to start camera manager: "
			  << strerror(-ret) << std::endl;
		return ret;
	}

	return 0;
}

void CamApp::cleanup()
{
	cm_->stop();
}

int CamApp::exec()
{
	int ret;

	ret = run();
	cleanup();

	return ret;
}

void CamApp::quit()
{
	loop_.exit();
}

int CamApp::parseOptions(int argc, char *argv[])
{
	StreamKeyValueParser streamKeyValue;

	OptionsParser parser;
	parser.addOption(OptCamera, OptionString,
			 "Specify which camera to operate on, by id or by index", "camera",
			 ArgumentRequired, "camera", true);
	parser.addOption(OptHelp, OptionNone, "Display this help message",
			 "help");
	parser.addOption(OptInfo, OptionNone,
			 "Display information about stream(s)", "info");
	parser.addOption(OptList, OptionNone, "List all cameras", "list");
	parser.addOption(OptListControls, OptionNone, "List cameras controls",
			 "list-controls");
	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
			 "list-properties");
	parser.addOption(OptMonitor, OptionNone,
			 "Monitor for hotplug and unplug camera events",
			 "monitor");

	/* Sub-options of OptCamera: */
	parser.addOption(OptCapture, OptionInteger,
			 "Capture until interrupted by user or until <count> frames captured",
			 "capture", ArgumentOptional, "count", false,
			 OptCamera);
#ifdef HAVE_KMS
	parser.addOption(OptDisplay, OptionString,
			 "Display viewfinder through DRM/KMS on specified connector",
			 "display", ArgumentOptional, "connector", false,
			 OptCamera);
#endif
	parser.addOption(OptFile, OptionString,
			 "Write captured frames to disk\n"
			 "If the file name ends with a '/', it sets the directory in which\n"
			 "to write files, using the default file name. Otherwise it sets the\n"
			 "full file path and name. The first '#' character in the file name\n"
			 "is expanded to the camera index, stream name and frame sequence number.\n"
			 "The default file name is 'frame-#.bin'.",
			 "file", ArgumentOptional, "filename", false,
			 OptCamera);
#ifdef HAVE_SDL
	parser.addOption(OptSDL, OptionNone, "Display viewfinder through SDL",
			 "sdl", ArgumentNone, "", false, OptCamera);
#endif
	parser.addOption(OptStream, &streamKeyValue,
			 "Set configuration of a camera stream", "stream", true,
			 OptCamera);
	parser.addOption(OptStrictFormats, OptionNone,
			 "Do not allow requested stream format(s) to be adjusted",
			 "strict-formats", ArgumentNone, nullptr, false,
			 OptCamera);
	parser.addOption(OptMetadata, OptionNone,
			 "Print the metadata for completed requests",
			 "metadata", ArgumentNone, nullptr, false,
			 OptCamera);
	parser.addOption(OptCaptureScript, OptionString,
			 "Load a capture session configuration script from a file",
			 "script", ArgumentRequired, "script", false,
			 OptCamera);

	options_ = parser.parse(argc, argv);
	if (!options_.valid())
		return -EINVAL;

	if (options_.empty() || options_.isSet(OptHelp)) {
		parser.usage();
		return options_.empty() ? -EINVAL : -EINTR;
	}

	return 0;
}

void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
{
	std::cout << "Camera Added: " << cam->id() << std::endl;
}

void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
{
	std::cout << "Camera Removed: " << cam->id() << std::endl;
}

void CamApp::captureDone()
{
	if (--loopUsers_ == 0)
		EventLoop::instance()->exit(0);
}

int CamApp::run()
{
	int ret;

	/* 1. List all cameras. */
	if (options_.isSet(OptList)) {
		std::cout << "Available cameras:" << std::endl;

		unsigned int index = 1;
		for (const std::shared_ptr<Camera> &cam : cm_->cameras()) {
			std::cout << index << ": " << cameraName(cam.get()) << std::endl;
			index++;
		}
	}

	/* 2. Create the camera sessions. */
	std::vector<std::unique_ptr<CameraSession>> sessions;

	if (options_.isSet(OptCamera)) {
		unsigned int index = 0;

		for (const OptionValue &camera : options_[OptCamera].toArray()) {
			std::unique_ptr<CameraSession> session =
				std::make_unique<CameraSession>(cm_.get(),
								camera.toString(),
								index,
								camera.children());
			if (!session->isValid()) {
				std::cout << "Failed to create camera session" << std::endl;
				return -EINVAL;
			}

			std::cout << "Using camera " << session->camera()->id()
				  << " as cam" << index << std::endl;

			session->captureDone.connect(this, &CamApp::captureDone);

			sessions.push_back(std::move(session));
			index++;
		}
	}

	/* 3. Print camera information. */
	if (options_.isSet(OptListControls) ||
	    options_.isSet(OptListProperties) ||
	    options_.isSet(OptInfo)) {
		for (const auto &session : sessions) {
			if (options_.isSet(OptListControls))
				session->listControls();
			if (options_.isSet(OptListProperties))
				session->listProperties();
			if (options_.isSet(OptInfo))
				session->infoConfiguration();
		}
	}

	/* 4. Start capture. */
	for (const auto &session : sessions) {
		if (!session->options().isSet(OptCapture))
			continue;

		ret = session->start();
		if (ret) {
			std::cout << "Failed to start camera session" << std::endl;
			return ret;
		}

		loopUsers_++;
	}

	/* 5. Enable hotplug monitoring. */
	if (options_.isSet(OptMonitor)) {
		std::cout << "Monitoring new hotplug and unplug events" << std::endl;
		std::cout << "Press Ctrl-C to interrupt" << std::endl;

		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);

		loopUsers_++;
	}

	if (loopUsers_)
		loop_.exec();

	/* 6. Stop capture. */
	for (const auto &session : sessions) {
		if (!session->options().isSet(OptCapture))
			continue;

		session->stop();
	}

	return 0;
}

std::string CamApp::cameraName(const Camera *camera)
{
	const ControlList &props = camera->properties();
	bool addModel = true;
	std::string name;

	/*
	 * Construct the name from the camera location, model and ID. The model
	 * is only used if the location isn't present or is set to External.
	 */
	if (props.contains(properties::Location)) {
		switch (props.get(properties::Location)) {
		case properties::CameraLocationFront:
			addModel = false;
			name = "Internal front camera ";
			break;
		case properties::CameraLocationBack:
			addModel = false;
			name = "Internal back camera ";
			break;
		case properties::CameraLocationExternal:
			name = "External camera ";
			break;
		}
	}

	if (addModel && props.contains(properties::Model)) {
		/*
		 * If the camera location is not availble use the camera model
		 * to build the camera name.
		 */
		name = "'" + props.get(properties::Model) + "' ";
	}

	name += "(" + camera->id() + ")";

	return name;
}

void signalHandler([[maybe_unused]] int signal)
{
	std::cout << "Exiting" << std::endl;
	CamApp::instance()->quit();
}

int main(int argc, char **argv)
{
	CamApp app;
	int ret;

	ret = app.init(argc, argv);
	if (ret)
		return ret == -EINTR ? 0 : EXIT_FAILURE;

	struct sigaction sa = {};
	sa.sa_handler = &signalHandler;
	sigaction(SIGINT, &sa, nullptr);

	if (app.exec())
		return EXIT_FAILURE;

	return 0;
}