[RFC,v1,19/20] pipeline: uvcvideo: Implement survey() and createCamera()
diff mbox series

Message ID 20260918080734.1228227-20-naush@raspberrypi.com
State New
Headers show
Series
  • libcamera: New enumeration API
Related show

Commit Message

Naushir Patuck Sept. 18, 2026, 7:59 a.m. UTC
Report a descriptor for every UVC camera in the system, identified by
the default video node of its media entity. The camera ID is generated
from the sysfs path derived from the video device numbers and the model
from the media device model, so no device is acquired or opened while
surveying.

createCamera() acquires the media device of the described camera and
creates it through the camera creation code previously in match(), which
has now been removed.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 123 +++++++++++++------
 1 file changed, 84 insertions(+), 39 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 5fa1bdb0ff0e..9d6dd1ec9bcc 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -21,12 +21,14 @@ 
 #include <libcamera/base/utils.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/camera_descriptor.h>
 #include <libcamera/control_ids.h>
 #include <libcamera/controls.h>
 #include <libcamera/property_ids.h>
 #include <libcamera/stream.h>
 
 #include "libcamera/internal/camera.h"
+#include "libcamera/internal/camera_descriptor.h"
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/media_device.h"
 #include "libcamera/internal/pipeline_handler.h"
@@ -46,7 +48,7 @@  public:
 	{
 	}
 
-	int init(std::shared_ptr<MediaDevice> media);
+	int init(std::shared_ptr<MediaDevice> media, const std::string &id);
 	void addControl(uint32_t cid, const ControlInfo &v4l2info,
 			ControlInfoMap::Map *ctrls);
 	void imageBufferReady(FrameBuffer *buffer);
@@ -62,8 +64,6 @@  public:
 	std::optional<v4l2_exposure_auto_type> manualExposureMode_;
 
 private:
-	bool generateId();
-
 	std::string id_;
 };
 
@@ -95,7 +95,9 @@  public:
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
-	bool match(DeviceEnumerator *enumerator) override;
+	int survey(const DeviceEnumerator *enumerator,
+		   std::vector<std::shared_ptr<CameraDescriptor>> *descriptors) override;
+	int createCamera(const CameraDescriptor *descriptor) override;
 
 private:
 	int processControl(const UVCCameraData *data, ControlList *controls,
@@ -129,6 +131,20 @@  std::optional<controls::ExposureTimeModeEnum> v4l2ToExposureMode(int32_t x)
 	}
 }
 
+/* Locate the default video entity of a UVC media device. */
+MediaEntity *defaultEntity(const MediaDevice *media)
+{
+	const std::vector<MediaEntity *> &entities = media->entities();
+	auto entity = std::find_if(entities.begin(), entities.end(),
+				   [](MediaEntity *e) {
+					   return e->flags() & MEDIA_ENT_FL_DEFAULT;
+				   });
+	if (entity == entities.end())
+		return nullptr;
+
+	return *entity;
+}
+
 /*
  * Generate the camera ID from the sysfs path of the UVC device video node. The
  * path is derived from the device numbers of the media entity, so the ID can be
@@ -136,12 +152,15 @@  std::optional<controls::ExposureTimeModeEnum> v4l2ToExposureMode(int32_t x)
  */
 std::string generateIdFromPath(const std::string &path)
 {
+	if (path.empty())
+		return {};
+
 	/* Create a controller ID from first device described in firmware. */
 	std::string controllerId;
 	std::string searchPath = path;
 	while (true) {
 		std::string::size_type pos = searchPath.rfind('/');
-		if (pos <= 1) {
+		if (pos == std::string::npos || pos <= 1) {
 			LOG(UVC, Error) << "Can not find controller ID";
 			return {};
 		}
@@ -533,31 +552,72 @@  int PipelineHandlerUVC::queueRequestDevice(Camera *camera, Request *request)
 	return 0;
 }
 
-bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
+int PipelineHandlerUVC::survey(const DeviceEnumerator *enumerator,
+			       std::vector<std::shared_ptr<CameraDescriptor>> *descriptors)
 {
-	std::shared_ptr<MediaDevice> media;
 	DeviceMatch dm("uvcvideo");
 
-	media = acquireMediaDevice(enumerator, dm);
-	if (!media)
-		return false;
+	for (std::shared_ptr<MediaDevice> &media : enumerator->searchAll(dm)) {
+		MediaEntity *entity = defaultEntity(media.get());
+		if (!entity)
+			continue;
 
-	std::unique_ptr<UVCCameraData> data = std::make_unique<UVCCameraData>(this);
+		/*
+		 * The sysfs path of the device is derived from the entity
+		 * device numbers, so the ID is generated without accessing
+		 * the video device.
+		 */
+		std::string id =
+			generateIdFromPath(sysfs::devicePath(entity->deviceMajor(),
+							     entity->deviceMinor()));
+		if (id.empty()) {
+			LOG(UVC, Warning)
+				<< "Failed to generate an ID for "
+				<< media->model();
+			continue;
+		}
 
-	if (data->init(media))
-		return false;
+		auto data = std::make_unique<CameraDescriptor::Private>();
+		data->id_ = std::move(id);
+		data->properties_.set(properties::Model,
+				      utils::toAscii(media->model()));
+		data->mediaDevices_ = { media };
+		data->entityName_ = entity->name();
+
+		descriptors->push_back(CameraDescriptor::create(std::move(data)));
+	}
+
+	return 0;
+}
+
+int PipelineHandlerUVC::createCamera(const CameraDescriptor *descriptor)
+{
+	const CameraDescriptor::Private *data = descriptor->_d();
+
+	if (data->mediaDevices_.size() != 1)
+		return -EINVAL;
+
+	std::shared_ptr<MediaDevice> media = data->mediaDevices_[0];
+	if (!acquireMediaDevice(media))
+		return -EBUSY;
+
+	std::unique_ptr<UVCCameraData> cameraData = std::make_unique<UVCCameraData>(this);
+
+	int ret = cameraData->init(media, descriptor->id());
+	if (ret)
+		return ret;
 
 	/* Create and register the camera. */
-	std::string id = data->id();
-	std::set<Stream *> streams{ &data->stream_ };
+	std::string id = cameraData->id();
+	std::set<Stream *> streams{ &cameraData->stream_ };
 	std::shared_ptr<Camera> camera =
-		Camera::create(std::move(data), id, streams);
+		Camera::create(std::move(cameraData), id, streams);
 	registerCamera(std::move(camera));
 
 	/* Enable hot-unplug notifications. */
-	hotplugMediaDevice(media);
+	hotplugMediaDevice(std::move(media));
 
-	return true;
+	return 0;
 }
 
 bool PipelineHandlerUVC::acquireDevice(Camera *camera)
@@ -577,35 +637,27 @@  void PipelineHandlerUVC::releaseDevice(Camera *camera)
 	data->video_->close();
 }
 
-int UVCCameraData::init(std::shared_ptr<MediaDevice> media)
+int UVCCameraData::init(std::shared_ptr<MediaDevice> media, const std::string &id)
 {
 	int ret;
 
+	id_ = id;
+
 	/* Locate and initialise the camera data with the default video node. */
-	const std::vector<MediaEntity *> &entities = media->entities();
-	auto entity = std::find_if(entities.begin(), entities.end(),
-				   [](MediaEntity *e) {
-					   return e->flags() & MEDIA_ENT_FL_DEFAULT;
-				   });
-	if (entity == entities.end()) {
+	MediaEntity *entity = defaultEntity(media.get());
+	if (!entity) {
 		LOG(UVC, Error) << "Could not find a default video device";
 		return -ENODEV;
 	}
 
 	/* Create and open the video device. */
-	video_ = std::make_unique<V4L2VideoDevice>(*entity);
+	video_ = std::make_unique<V4L2VideoDevice>(entity);
 	ret = video_->open();
 	if (ret)
 		return ret;
 
 	video_->bufferReady.connect(this, &UVCCameraData::imageBufferReady);
 
-	/* Generate the camera ID. */
-	if (!generateId()) {
-		LOG(UVC, Error) << "Failed to generate camera ID";
-		return -EINVAL;
-	}
-
 	/*
 	 * Populate the map of supported formats, and infer the camera sensor
 	 * resolution from the largest size it advertises.
@@ -692,13 +744,6 @@  int UVCCameraData::init(std::shared_ptr<MediaDevice> media)
 	return 0;
 }
 
-bool UVCCameraData::generateId()
-{
-	id_ = generateIdFromPath(video_->devicePath());
-
-	return !id_.empty();
-}
-
 void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 			       ControlInfoMap::Map *ctrls)
 {