The MediaDevice class will be the entry point to hot-unplug, as it corresponds to the kernel devices that will report device removal events. The class will signal media device disconnection to pipeline handlers, which will clean up resources as a result. This can't be performed synchronously as references may exist to the related Camera objects in applications. The MediaDevice object thus needs to be reference-counted in order to support unplugging, as otherwise pipeline handlers would be required to drop all the references to the media device they have borrowed synchronously with the disconnection signal handler, which would be very error prone (if even possible at all in a sane way). Handle MedieDevice instances with std::shared_ptr<> to support this. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
246 lines
6.1 KiB
C++
246 lines
6.1 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/*
|
|
* Copyright (C) 2019, Google Inc.
|
|
*
|
|
* media_device_link_test.cpp - Tests link handling on VIMC media device
|
|
*/
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
#include "device_enumerator.h"
|
|
#include "media_device.h"
|
|
|
|
#include "test.h"
|
|
|
|
using namespace libcamera;
|
|
using namespace std;
|
|
|
|
/*
|
|
* This link test requires a vimc device in order to exercise the
|
|
* MediaObject link handling API on a graph with a predetermined topology.
|
|
*
|
|
* vimc is a Media Controller kernel driver that creates virtual devices.
|
|
* From a userspace point of view they appear as normal media controller
|
|
* devices, but are not backed by any particular piece of hardware. They can
|
|
* thus be used for testing purpose without depending on a particular hardware
|
|
* platform.
|
|
*
|
|
* If no vimc device is found (most likely because the vimc driver is not
|
|
* loaded) the test is skipped.
|
|
*/
|
|
|
|
class MediaDeviceLinkTest : public Test
|
|
{
|
|
int init()
|
|
{
|
|
enumerator = unique_ptr<DeviceEnumerator>(DeviceEnumerator::create());
|
|
if (!enumerator) {
|
|
cerr << "Failed to create device enumerator" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (enumerator->enumerate()) {
|
|
cerr << "Failed to enumerate media devices" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
DeviceMatch dm("vimc");
|
|
dev_ = enumerator->search(dm);
|
|
if (!dev_) {
|
|
cerr << "No VIMC media device found: skip test" << endl;
|
|
return TestSkip;
|
|
}
|
|
|
|
dev_->acquire();
|
|
|
|
if (dev_->open()) {
|
|
cerr << "Failed to open media device at "
|
|
<< dev_->deviceNode() << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int run()
|
|
{
|
|
/*
|
|
* First of all disable all links in the media graph to
|
|
* ensure we start from a known state.
|
|
*/
|
|
if (dev_->disableLinks()) {
|
|
cerr << "Failed to disable all links in the media graph";
|
|
return TestFail;
|
|
}
|
|
|
|
/*
|
|
* Test if link can be consistently retrieved through the
|
|
* different methods the media device offers.
|
|
*/
|
|
string linkName("'Debayer A':[1] -> 'Scaler':[0]'");
|
|
MediaLink *link = dev_->link("Debayer A", 1, "Scaler", 0);
|
|
if (!link) {
|
|
cerr << "Unable to find link: " << linkName
|
|
<< " using lookup by name" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
MediaEntity *source = dev_->getEntityByName("Debayer A");
|
|
if (!source) {
|
|
cerr << "Unable to find entity: 'Debayer A'" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
MediaEntity *sink = dev_->getEntityByName("Scaler");
|
|
if (!sink) {
|
|
cerr << "Unable to find entity: 'Scaler'" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
MediaLink *link2 = dev_->link(source, 1, sink, 0);
|
|
if (!link2) {
|
|
cerr << "Unable to find link: " << linkName
|
|
<< " using lookup by entity" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (link != link2) {
|
|
cerr << "Link lookup by name and by entity don't match"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
link2 = dev_->link(source->getPadByIndex(1),
|
|
sink->getPadByIndex(0));
|
|
if (!link2) {
|
|
cerr << "Unable to find link: " << linkName
|
|
<< " using lookup by pad" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (link != link2) {
|
|
cerr << "Link lookup by name and by pad don't match"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* After reset the link shall not be enabled. */
|
|
if (link->flags() & MEDIA_LNK_FL_ENABLED) {
|
|
cerr << "Link " << linkName
|
|
<< " should not be enabled after a device reset"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* Enable the link and test if enabling was successful. */
|
|
if (link->setEnabled(true)) {
|
|
cerr << "Failed to enable link: " << linkName
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (!(link->flags() & MEDIA_LNK_FL_ENABLED)) {
|
|
cerr << "Link " << linkName
|
|
<< " was enabled but it is reported as disabled"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* Disable the link and test if disabling was successful. */
|
|
if (link->setEnabled(false)) {
|
|
cerr << "Failed to disable link: " << linkName
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (link->flags() & MEDIA_LNK_FL_ENABLED) {
|
|
cerr << "Link " << linkName
|
|
<< " was disabled but it is reported as enabled"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* Try to get a non existing link. */
|
|
linkName = "'Sensor A':[1] -> 'Scaler':[0]";
|
|
link = dev_->link("Sensor A", 1, "Scaler", 0);
|
|
if (link) {
|
|
cerr << "Link lookup for " << linkName
|
|
<< " succeeded but link does not exist"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* Now get an immutable link and try to disable it. */
|
|
linkName = "'Sensor A':[0] -> 'Raw Capture 0':[0]";
|
|
link = dev_->link("Sensor A", 0, "Raw Capture 0", 0);
|
|
if (!link) {
|
|
cerr << "Unable to find link: " << linkName
|
|
<< " using lookup by name" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (!(link->flags() & MEDIA_LNK_FL_IMMUTABLE)) {
|
|
cerr << "Link " << linkName
|
|
<< " should be 'IMMUTABLE'" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/* Disabling an immutable link shall fail. */
|
|
if (!link->setEnabled(false)) {
|
|
cerr << "Disabling immutable link " << linkName
|
|
<< " succeeded but should have failed" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
/*
|
|
* Enable an disabled link, and verify it is disabled again
|
|
* after disabling all links in the media graph.
|
|
*/
|
|
linkName = "'Debayer B':[1] -> 'Scaler':[0]'";
|
|
link = dev_->link("Debayer B", 1, "Scaler", 0);
|
|
if (!link) {
|
|
cerr << "Unable to find link: " << linkName
|
|
<< " using lookup by name" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (link->setEnabled(true)) {
|
|
cerr << "Failed to enable link: " << linkName
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (!(link->flags() & MEDIA_LNK_FL_ENABLED)) {
|
|
cerr << "Link " << linkName
|
|
<< " was enabled but it is reported as disabled"
|
|
<< endl;
|
|
return TestFail;
|
|
}
|
|
|
|
if (dev_->disableLinks()) {
|
|
cerr << "Failed to disable all links in the media graph";
|
|
return TestFail;
|
|
}
|
|
|
|
if (link->flags() & MEDIA_LNK_FL_ENABLED) {
|
|
cerr << "All links in the media graph have been disabled"
|
|
<< " but link " << linkName
|
|
<< " is still reported as enabled" << endl;
|
|
return TestFail;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanup()
|
|
{
|
|
dev_->close();
|
|
dev_->release();
|
|
}
|
|
|
|
private:
|
|
unique_ptr<DeviceEnumerator> enumerator;
|
|
shared_ptr<MediaDevice> dev_;
|
|
};
|
|
|
|
TEST_REGISTER(MediaDeviceLinkTest);
|