From patchwork Mon Dec 31 09:27:50 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 114 Return-Path: Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7107360B33 for ; Mon, 31 Dec 2018 10:27:57 +0100 (CET) X-Originating-IP: 87.16.51.54 Received: from uno.homenet.telecomitalia.it (host54-51-dynamic.16-87-r.retail.telecomitalia.it [87.16.51.54]) (Authenticated sender: jacopo@jmondi.org) by relay6-d.mail.gandi.net (Postfix) with ESMTPSA id 2A78DC0003; Mon, 31 Dec 2018 09:27:55 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Mon, 31 Dec 2018 10:27:50 +0100 Message-Id: <20181231092752.5018-2-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20181231092752.5018-1-jacopo@jmondi.org> References: <20181231092752.5018-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 1/3] libcamera: Add MediaObject class hierarchy X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 31 Dec 2018 09:27:57 -0000 Add a class hierarcy to represent all media objects a media graph represents. Add a base MediaObject class, which retains the global unique object id, and define the derived MediaEntity, MediaLink and MediaPad classes. This hierarchy will be used by the MediaDevice objects which represents and handles the media graph. Reviewed-by: Laurent Pinchart Signed-off-by: Jacopo Mondi --- src/libcamera/include/media_object.h | 106 ++++++++++ src/libcamera/media_object.cpp | 279 +++++++++++++++++++++++++++ src/libcamera/meson.build | 2 + 3 files changed, 387 insertions(+) create mode 100644 src/libcamera/include/media_object.h create mode 100644 src/libcamera/media_object.cpp diff --git a/src/libcamera/include/media_object.h b/src/libcamera/include/media_object.h new file mode 100644 index 0000000..a8bdaab --- /dev/null +++ b/src/libcamera/include/media_object.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_object.h - Media Device objects: entities, pads and links. + */ +#ifndef __LIBCAMERA_MEDIA_OBJECT_H__ +#define __LIBCAMERA_MEDIA_OBJECT_H__ + +#include +#include + +#include + +namespace libcamera { + +class MediaDevice; +class MediaEntity; + +class MediaObject +{ +public: + MediaObject(unsigned int id) : id_(id) { } + virtual ~MediaObject() { } + + unsigned int id() const { return id_; } + +protected: + unsigned int id_; +}; + +class MediaPad; +class MediaLink : public MediaObject +{ + friend class MediaDevice; + +public: + ~MediaLink() { } + + MediaPad *source() const { return source_; } + MediaPad *sink() const { return sink_; } + unsigned int flags() const { return flags_; } + +private: + MediaLink(const struct media_v2_link *link, + MediaPad *source, MediaPad *sink); + MediaLink(const MediaLink &) = delete; + + MediaPad *source_; + MediaPad *sink_; + unsigned int flags_; +}; + +class MediaEntity; +class MediaPad : public MediaObject +{ + friend class MediaDevice; + +public: + ~MediaPad(); + + unsigned int index() const { return index_; } + MediaEntity *entity() const { return entity_; } + unsigned int flags() const { return flags_; } + const std::vector &links() const { return links_; } + + void addLink(MediaLink *link); + +private: + MediaPad(const struct media_v2_pad *pad, MediaEntity *entity); + MediaPad(const MediaPad &) = delete; + + unsigned int index_; + MediaEntity *entity_; + unsigned int flags_; + + std::vector links_; +}; + +class MediaEntity : public MediaObject +{ + friend class MediaDevice; + +public: + const std::string &name() const { return name_; } + + const std::vector &pads() { return pads_; } + + const MediaPad *getPadByIndex(unsigned int index); + const MediaPad *getPadById(unsigned int id); + +private: + MediaEntity(const struct media_v2_entity *entity); + MediaEntity(const MediaEntity &) = delete; + ~MediaEntity(); + + std::string name_; + std::string devnode_; + + std::vector pads_; + + void addPad(MediaPad *pad); +}; + +} /* namespace libcamera */ +#endif /* __LIBCAMERA_MEDIA_OBJECT_H__ */ diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp new file mode 100644 index 0000000..5f1b2a2 --- /dev/null +++ b/src/libcamera/media_object.cpp @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_object.cpp - Media device objects: entities, pads and links + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include "log.h" +#include "media_object.h" + +/** + * \file media_object.h + * + * Provides a class hierarchy that represent the corresponding of media + * objects exposed by the kernel's media controller APIs. + * + * Media Objects represented here are media entities, media pads and + * media links, created with informations as obtained by the + * MEDIA_IOC_G_TOPOLOGY ioctl call. + * + * MediaLink, MediaPad and MediaEntity are derived classes of the virtual + * base class MediaObject, which maintains a globally unique id for each object. + * + * Media objects have private constructors to restrict the number of classes + * that can instantiate them only to the 'friend' MediaDevice one. Media + * objects are in facts created when a MediaDevice gets populated. + */ + +namespace libcamera { + +/** + * \class MediaObject + * \brief Base class for all media object types + * + * MediaObject is the virtual base class for all media objects in the + * media graph. Each object has a globally unique id assigned, and this + * base class provides that. + * + * MediaEntities, MediaPads and MediaLink are MediaObject derived classes. + */ + +/** + * \fn MediaObject::MediaObject() + * \brief Construct a MediaObject with \a id + * \param id The globally unique media object id + */ + +/** + * \fn MediaObject::id() + * \brief Retrieve the media object global id + * \return The media object globally unique id + */ + +/** + * \var MediaObject::id_ + * \brief The MediaObject globally unique id + */ + +/** + * \class MediaLink + * \brief Media Link object + * + * A MediaLink represents a 'struct media_v2_link', with associated + * references to the MediaPad it connects and an internal status defined + * by the MEDIA_LNK_FL_* flags captured in flags_. + * + * MediaLink can be enabled and disabled, with the exception of + * immutable links (see MEDIA_LNK_FL_IMMUTABLE). + * + * MediaLinks are created between MediaPads inspecting the media graph + * topology and are stored in both the source and sink MediaPad they + * connect. + */ + +/** + * \brief Construct a MediaLink + * \param link The media link representation + * \param source The MediaPad source + * \param sink The MediaPad sink + */ +MediaLink::MediaLink(const struct media_v2_link *link, MediaPad *source, + MediaPad *sink) + : MediaObject(link->id), source_(source), + sink_(sink), flags_(link->flags) +{ +} + +/** + * \fn MediaLink::source() + * \brief Retrieve the media link source pad + * \return The source MediaPad + */ + +/** + * \fn MediaLink::sink() + * \brief Retrieve the media link sink pad + * \return The sinks MediaPad + */ + +/** + * \fn MediaLink::flags() + * \brief Retrieve the media link flags + * \return The link flags (MEDIA_LNK_FL_*) + */ + +/** + * \class MediaPad + * \brief Media Pad object + * + * MediaPads represent a 'struct media_v2_pad', with associated list of + * links and a reference to the entity they belong to. + * + * MediaPads are connected to other MediaPads by MediaLinks, created + * inspecting the media graph topology. Data connection between pads can be + * enabled or disabled operating on the link that connects the two. See + * MediaLink. + * + * MediaPads are either 'source' pads, or 'sink' pads. This information is + * captured by the MEDIA_PAD_FL_* flags as stored in the flags_ member + * variable. + * + * Each MediaPad has a list of MediaLinks it is associated to. All links + * in a source pad are outbound links, while all links in a sink pad are + * inbound ones. + * + * Each MediaPad has a reference to the MediaEntity it belongs to. + */ + +/** + * \brief Create a MediaPad + * \param pad The struct media_v2_pad kernel representation + * \param entity The MediaEntity this pad belongs to + */ +MediaPad::MediaPad(const struct media_v2_pad *pad, MediaEntity *entity) + : MediaObject(pad->id), index_(pad->index), entity_(entity), + flags_(pad->flags) +{ +} + +/** + * \brief Release the list of links in the pad + * + * This function does not delete the MediaLink pointed by the links_ list + * entries. + */ +MediaPad::~MediaPad() +{ + links_.clear(); +} + +/** + * \fn MediaPad::index() + * \brief Retrieve the media pad index + * \return The 0-indexed MediaPad index + */ + +/** + * \fn MediaPad::entity() + * \brief Retrieve the entity the media pad belongs to + * \return The MediaEntity the pad belongs to + */ + +/** + * \fn MediaPad::flags() + * \brief Retrieve the media pad flags + * \return The MediaPad flags (MEDIA_PAD_FL_*) + */ + +/** + * \fn MediaPad::links() + * \brief Retrieve all links in the media pad + * \return A list of MediaLinks associated with the media pad + */ + +/** + * \brief Add a new link to this pad + * \param link The MediaLink to add + */ +void MediaPad::addLink(MediaLink *link) +{ + links_.push_back(link); +} + +/** + * \class MediaEntity + * \brief Media entity + * + * A MediaEntity represents a 'struct media_v2_entity' with an associated + * list of MediaPads it contains. + * + * Entities are created inspecting the media graph topology, and MediaPads + * gets added as they are discovered. + * + * TODO: Add support for associating a devnode to the entity when integrating + * with DeviceEnumerator. + */ + +/** + * \fn MediaEntity::name() + * \brief Retrieve the entity name + * \return The MediaEntity name + */ + +/** + * \fn MediaEntity::pads() + * \brief Retrieve all pads of a media entity + * \return The list of the entity MediaPads + */ + +/** + * \brief Get a pad in this entity by its index + * \param index The 0-indexed pad index + * \return The pad with index \a index + * \return nullptr if no pad is found at \a index + */ +const MediaPad *MediaEntity::getPadByIndex(unsigned int index) +{ + for (MediaPad *p : pads_) + if (p->index() == index) + return p; + + return nullptr; +} + +/** + * \brief Get a pad in this entity by its globally unique id + * \param id The pad globally unique id + * \return The pad with id \a id + * \return nullptr if not pad with \a id is found + */ +const MediaPad *MediaEntity::getPadById(unsigned int id) +{ + for (MediaPad *p : pads_) + if (p->id() == id) + return p; + + return nullptr; +} + +/** + * \brief Construct a MediaEntity + * \param entity The struct media_v2_entity kernel representation + */ +MediaEntity::MediaEntity(const struct media_v2_entity *entity) + : MediaObject(entity->id), name_(entity->name) +{ +} + +/** + * \brief Release the list of pads in the entity + * + * This function does not delete the MediaPad instances pointed to by the + * pads_ list entries. + */ +MediaEntity::~MediaEntity() +{ + pads_.clear(); +} + +/** + * \brief Add \a pad to list of entity's pads + * \param pad The MediaPad to add + */ +void MediaEntity::addPad(MediaPad *pad) +{ + pads_.push_back(pad); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index ac5bba0..2985d40 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -4,12 +4,14 @@ libcamera_sources = files([ 'device_enumerator.cpp', 'log.cpp', 'main.cpp', + 'media_object.cpp', 'pipeline_handler.cpp', ]) libcamera_headers = files([ 'include/device_enumerator.h', 'include/log.h', + 'include/media_object.h', 'include/pipeline_handler.h', 'include/utils.h', ]) From patchwork Mon Dec 31 09:27:51 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 115 Return-Path: Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id B34EE60B30 for ; Mon, 31 Dec 2018 10:27:58 +0100 (CET) X-Originating-IP: 87.16.51.54 Received: from uno.homenet.telecomitalia.it (host54-51-dynamic.16-87-r.retail.telecomitalia.it [87.16.51.54]) (Authenticated sender: jacopo@jmondi.org) by relay6-d.mail.gandi.net (Postfix) with ESMTPSA id A0292C000F; Mon, 31 Dec 2018 09:27:57 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Mon, 31 Dec 2018 10:27:51 +0100 Message-Id: <20181231092752.5018-3-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20181231092752.5018-1-jacopo@jmondi.org> References: <20181231092752.5018-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 2/3] libcamera: Add MediaDevice class X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 31 Dec 2018 09:27:58 -0000 The MediaDevice object implements handling and configuration of the media graph associated with a media device. The class allows enumeration of all pads, links and entities registered in the media graph. Signed-off-by: Jacopo Mondi --- src/libcamera/include/media_device.h | 61 +++++ src/libcamera/media_device.cpp | 362 +++++++++++++++++++++++++++ src/libcamera/meson.build | 2 + 3 files changed, 425 insertions(+) create mode 100644 src/libcamera/include/media_device.h create mode 100644 src/libcamera/media_device.cpp diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h new file mode 100644 index 0000000..11eae5c --- /dev/null +++ b/src/libcamera/include/media_device.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_device.h - Media device handler + */ +#ifndef __LIBCAMERA_MEDIA_DEVICE_H__ +#define __LIBCAMERA_MEDIA_DEVICE_H__ + +#include +#include +#include +#include + +#include + +#include "media_object.h" + +namespace libcamera { + +class MediaDevice +{ +public: + MediaDevice() : fd_(-1) { }; + ~MediaDevice(); + + const std::string driver() const { return driver_; } + const std::string devnode() const { return devnode_; } + + int open(const std::string &devnode); + void close(); + + const std::vector &entities() const { return entities_; } + + int populate(); + +private: + std::string driver_; + std::string devnode_; + int fd_; + + /* + * Global map of media objects (entities, pads, links) associated to + * their globally unique id. + */ + std::map objects_; + MediaObject *getObject(unsigned int id); + int addObject(MediaObject *obj); + void deleteObjects(); + + /* Global list of media entities in the media graph: lookup by name. */ + std::vector entities_; + MediaEntity *getEntityByName(const std::string &name); + + void populateEntities(const struct media_v2_topology &topology); + int populatePads(const struct media_v2_topology &topology); + int populateLinks(const struct media_v2_topology &topology); +}; + +} /* namespace libcamera */ +#endif /* __LIBCAMERA_MEDIA_DEVICE_H__ */ diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp new file mode 100644 index 0000000..2a293bd --- /dev/null +++ b/src/libcamera/media_device.cpp @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_device.cpp - Media device handler + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "log.h" +#include "media_device.h" + +/** + * \file media_device.h + */ +namespace libcamera { + +/** + * \class MediaDevice + * \brief Media device handler + * + * Represents the graph of media objects associated with a media device as + * exposed by the kernel through the media controller APIs. + * + * The caller is responsible for opening the MediaDevice explicitly before + * operating on it, and shall close it when not needed anymore, as access + * to the MediaDevice is exclusive. + * + * A MediaDevice is created empty and gets populated by inspecting the media + * graph topology using the MEDIA_IOC_G_TOPOLOGY ioctls. Representation + * of each entity, pad and link described are created using MediaObject + * derived classes. + * + * All MediaObject are stored in a global pool, where they could be retrieved + * from by their globally unique id. + * + * References to MediaEntity registered in the graph are stored in a vector + * to allow easier by-name lookup, and the list of MediaEntities is accessible. + */ + +/** + * \brief Close the media device file descriptor and delete all object + */ +MediaDevice::~MediaDevice() +{ + if (fd_ != -1) + ::close(fd_); + deleteObjects(); +} + +/** + * \fn MediaDevice::driver() + * \brief Retrieve the media device driver name + * \return The name of the kernel driver that handles the MediaDevice + */ + +/** + * \fn MediaDevice::devnode() + * \brief Retrieve the media device device node path + * \return The MediaDevice devnode path + */ + +/** + * \brief Delete all media objects in the MediaDevice. + * + * Delete all MediaEntities; entities will then delete their pads, + * and each source pad will delete links. + * + * After this function has been called, the media graph will be unpopulated + * and its media objects deleted. The media device has to be populated + * before it could be used again. + */ +void MediaDevice::deleteObjects() +{ + for (auto const &o : objects_) + delete o.second; + + objects_.clear(); + entities_.clear(); +} + +/** + * \brief Open a media device and retrieve informations from it + * \param devnode The media device node path + * + * The function fails if the media device is already open or if either + * open or the media device information retrieval operations fail. + * + * \return 0 for success or a negative error number otherwise + */ +int MediaDevice::open(const std::string &devnode) +{ + if (fd_ != -1) { + LOG(Error) << "MediaDevice already open"; + return -EBUSY; + } + + int ret = ::open(devnode.c_str(), O_RDWR); + if (ret < 0) { + ret = -errno; + LOG(Error) << "Failed to open media device at " << devnode + << ": " << strerror(-ret); + return ret; + } + fd_ = ret; + devnode_ = devnode; + + struct media_device_info info = { }; + ret = ioctl(fd_, MEDIA_IOC_DEVICE_INFO, &info); + if (ret) { + ret = -errno; + LOG(Error) << "Failed to get media device info " + << ": " << strerror(-ret); + return ret; + } + + driver_ = info.driver; + + return 0; +} + +/** + * \brief Close the file descriptor associated with the media device. + * + * After this function has been called, for the MediaDevice to be operated on, + * the caller shall open it again. + */ +void MediaDevice::close() +{ + if (fd_ == -1) + return; + + ::close(fd_); + fd_ = -1; +} + +/** + * \fn MediaDevice::entities() + * \brief Retrieve the list of entities in the media graph + * \return The list of MediaEntities registered in the MediaDevice + */ + +/* + * Add a new object to the global objects pool and fail if the object + * has already been registered. + */ +int MediaDevice::addObject(MediaObject *obj) +{ + + if (objects_.find(obj->id()) != objects_.end()) { + LOG(Error) << "Element with id " << obj->id() + << " already enumerated."; + return -EEXIST; + } + + objects_[obj->id()] = obj; + + return 0; +} + +/* + * MediaObject pool lookup by id. + */ +MediaObject *MediaDevice::getObject(unsigned int id) +{ + auto it = objects_.find(id); + return (it == objects_.end()) ? nullptr : it->second; +} + +/** + * \brief Return the MediaEntity with name \a name + * \param name The entity name + * \return The entity with \a name + * \return nullptr if no entity with \a name is found + */ +MediaEntity *MediaDevice::getEntityByName(const std::string &name) +{ + for (MediaEntity *e : entities_) + if (e->name() == name) + return e; + + return nullptr; +} + +int MediaDevice::populateLinks(const struct media_v2_topology &topology) +{ + media_v2_link *mediaLinks = reinterpret_cast + (topology.ptr_links); + + for (unsigned int i = 0; i < topology.num_links; ++i) { + /* + * Skip links between entities and interfaces: we only care + * about pad-2-pad links here. + */ + if ((mediaLinks[i].flags & MEDIA_LNK_FL_LINK_TYPE) == + MEDIA_LNK_FL_INTERFACE_LINK) + continue; + + /* Store references to source and sink pads in the link. */ + unsigned int source_id = mediaLinks[i].source_id; + MediaPad *source = dynamic_cast + (getObject(source_id)); + if (!source) { + LOG(Error) << "Failed to find pad with id: " + << source_id; + return -ENODEV; + } + + unsigned int sink_id = mediaLinks[i].sink_id; + MediaPad *sink = dynamic_cast + (getObject(sink_id)); + if (!sink) { + LOG(Error) << "Failed to find pad with id: " + << sink_id; + return -ENODEV; + } + + MediaLink *link = new MediaLink(&mediaLinks[i], source, sink); + source->addLink(link); + sink->addLink(link); + + addObject(link); + } + + return 0; +} + +int MediaDevice::populatePads(const struct media_v2_topology &topology) +{ + media_v2_pad *mediaPads = reinterpret_cast + (topology.ptr_pads); + + for (unsigned int i = 0; i < topology.num_pads; ++i) { + unsigned int entity_id = mediaPads[i].entity_id; + + /* Store a reference to this MediaPad in entity. */ + MediaEntity *mediaEntity = dynamic_cast + (getObject(entity_id)); + if (!mediaEntity) { + LOG(Error) << "Failed to find entity with id: " + << entity_id; + return -ENODEV; + } + + MediaPad *pad = new MediaPad(&mediaPads[i], mediaEntity); + mediaEntity->addPad(pad); + + addObject(pad); + } + + return 0; +} + +/* + * For each entity in the media graph create a MediaEntity and store a + * reference in the MediaObject global pool and in the global vector of + * entities. + */ +void MediaDevice::populateEntities(const struct media_v2_topology &topology) +{ + media_v2_entity *mediaEntities = reinterpret_cast + (topology.ptr_entities); + + for (unsigned int i = 0; i < topology.num_entities; ++i) { + MediaEntity *entity = new MediaEntity(&mediaEntities[i]); + + addObject(entity); + entities_.push_back(entity); + } +} + +/** + * \brief Populate the media graph with media objects + * + * This function enumerates all media objects in the media device graph and + * creates their MediaObject representations. All entities, pads and links are + * stored as MediaEntity, MediaPad and MediaLink respectively, with cross- + * references between objects. Interfaces are not processed. + * + * MediaEntities are stored in a global list in the MediaDevice itself to ease + * lookup, while MediaPads are accessible from the MediaEntity they belong + * to only and MediaLinks from the MediaPad they connect. + * + * \return 0 on success, a negative error code otherwise + */ +int MediaDevice::populate() +{ + struct media_v2_topology topology; + struct media_v2_entity *ents; + struct media_v2_link *links; + struct media_v2_pad *pads; + int ret; + + /* + * Keep calling G_TOPOLOGY until the version number stays stable. + */ + while (true) { + topology = {}; + + ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology); + if (ret < 0) { + ret = -errno; + LOG(Error) << "Failed to enumerate topology: " + << strerror(-ret); + return ret; + } + + __u64 version = topology.topology_version; + + ents = new media_v2_entity[topology.num_entities]; + links = new media_v2_link[topology.num_links]; + pads = new media_v2_pad[topology.num_pads]; + + topology.ptr_entities = reinterpret_cast<__u64>(ents); + topology.ptr_links = reinterpret_cast<__u64>(links); + topology.ptr_pads = reinterpret_cast<__u64>(pads); + + ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology); + if (ret < 0) { + ret = -errno; + LOG(Error) << "Failed to enumerate topology: " + << strerror(-ret); + goto error_free_objs; + } + + if (version == topology.topology_version) + break; + + delete[] links; + delete[] ents; + delete[] pads; + } + + /* Populate entities, pads and links. */ + populateEntities(topology); + + ret = populatePads(topology); + if (ret) + goto error_free_objs; + + ret = populateLinks(topology); +error_free_objs: + if (ret) + deleteObjects(); + + delete[] links; + delete[] ents; + delete[] pads; + + return ret; +} + +} /* namespace libcamera */ diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index 2985d40..2ff5bb5 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -4,6 +4,7 @@ libcamera_sources = files([ 'device_enumerator.cpp', 'log.cpp', 'main.cpp', + 'media_device.cpp', 'media_object.cpp', 'pipeline_handler.cpp', ]) @@ -11,6 +12,7 @@ libcamera_sources = files([ libcamera_headers = files([ 'include/device_enumerator.h', 'include/log.h', + 'include/media_device.h', 'include/media_object.h', 'include/pipeline_handler.h', 'include/utils.h', From patchwork Mon Dec 31 09:27:52 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 116 Return-Path: Received: from relay6-d.mail.gandi.net (relay6-d.mail.gandi.net [217.70.183.198]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A7A7F60B37 for ; Mon, 31 Dec 2018 10:27:59 +0100 (CET) X-Originating-IP: 87.16.51.54 Received: from uno.homenet.telecomitalia.it (host54-51-dynamic.16-87-r.retail.telecomitalia.it [87.16.51.54]) (Authenticated sender: jacopo@jmondi.org) by relay6-d.mail.gandi.net (Postfix) with ESMTPSA id E4108C000E; Mon, 31 Dec 2018 09:27:58 +0000 (UTC) From: Jacopo Mondi To: libcamera-devel@lists.libcamera.org Date: Mon, 31 Dec 2018 10:27:52 +0100 Message-Id: <20181231092752.5018-4-jacopo@jmondi.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20181231092752.5018-1-jacopo@jmondi.org> References: <20181231092752.5018-1-jacopo@jmondi.org> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v4 3/3] test: Add media device test X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 31 Dec 2018 09:27:59 -0000 Add media device test infrastructure and an intial test that print out the media devices available in the system. Signed-off-by: Jacopo Mondi --- test/media_device/media_device_test.cpp | 175 ++++++++++++++++++++++++ test/media_device/meson.build | 5 + test/meson.build | 4 + 3 files changed, 184 insertions(+) create mode 100644 test/media_device/media_device_test.cpp create mode 100644 test/media_device/meson.build diff --git a/test/media_device/media_device_test.cpp b/test/media_device/media_device_test.cpp new file mode 100644 index 0000000..2df4552 --- /dev/null +++ b/test/media_device/media_device_test.cpp @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2018, Google Inc. + * + * media_device_test.cpp - Tests for the media device class. + * + * Test library for the media device class. + */ +#include + +#include +#include +#include + +#include "media_device.h" + +#include "test.h" + +using namespace libcamera; +using namespace std; + +/* + * MediaDeviceTest object: runs a sequence of tests on all media + * devices found in the system. + * + * If no accessible media device is found, the test is skipped. + */ +class MediaDeviceTest : public Test +{ +public: + MediaDeviceTest() { } + ~MediaDeviceTest() { } + +protected: + int init() { return 0; } + int run(); + void cleanup() { } + +private: + int testMediaDevice(string devnode); + + void printMediaGraph(const MediaDevice &media, ostream &os); + void printLinkFlags(const MediaLink *link, ostream &os); + void printNode(const MediaPad *pad, ostream &os); +}; + +void MediaDeviceTest::printNode(const MediaPad *pad, ostream &os) +{ + const MediaEntity *entity = pad->entity(); + + os << "\"" << entity->name() << "\"[" + << pad->index() << "]"; +} + +void MediaDeviceTest::printLinkFlags(const MediaLink *link, ostream &os) +{ + unsigned int flags = link->flags(); + + os << " ["; + if (flags) { + os << (flags & MEDIA_LNK_FL_ENABLED ? "ENABLED," : "") + << (flags & MEDIA_LNK_FL_IMMUTABLE ? "IMMUTABLE" : ""); + } + os << "]\n"; +} + +/* + * For each entity in the media graph, printout links directed to its sinks + * and source pads. + */ +void MediaDeviceTest::printMediaGraph(const MediaDevice &media, ostream &os) +{ + os << "\n" << media.driver() << " - " << media.devnode() << "\n\n"; + + for (auto const &entity : media.entities()) { + os << "\"" << entity->name() << "\"\n"; + + for (auto const &sink : entity->pads()) { + if (!(sink->flags() & MEDIA_PAD_FL_SINK)) + continue; + + os << " [" << sink->index() << "]" << ": Sink\n"; + for (auto const &link : sink->links()) { + os << "\t"; + printNode(sink, os); + os << " <- "; + printNode(link->source(), os); + printLinkFlags(link, os); + } + os << "\n"; + } + + for (auto const &source : entity->pads()) { + if (!(source->flags() & MEDIA_PAD_FL_SOURCE)) + continue; + + os << " [" << source->index() << "]" << ": Source\n"; + for (auto const &link : source->links()) { + os << "\t"; + printNode(source, os); + os << " -> "; + printNode(link->sink(), os); + printLinkFlags(link, os); + } + os << "\n"; + } + } + + os.flush(); +} + +/* Test a single media device. */ +int MediaDeviceTest::testMediaDevice(const string devnode) +{ + MediaDevice dev; + int ret; + + /* Fuzzy open/close sequence. */ + ret = dev.open(devnode); + if (ret) + return ret; + + ret = dev.open(devnode); + if (!ret) + return ret; + + dev.close(); + dev.close(); + + ret = dev.open(devnode); + if (ret) + return ret; + + ret = dev.populate(); + if (ret) + return ret; + + /* Run tests in sequence. */ + printMediaGraph(dev, cerr); + /* TODO: add more tests here. */ + + dev.close(); + + return 0; +} + +/* Run tests on all media devices. */ +#define MAX_MEDIA_DEV 256 +int MediaDeviceTest::run() +{ + const string devnode("/dev/media"); + unsigned int i; + int ret = 77; /* skip test exit code */ + + /* + * Run the test sequence on all media device found in the + * system, if any. + */ + for (i = 0; i < MAX_MEDIA_DEV; i++) { + string mediadev = devnode + to_string(i); + struct stat pstat = { }; + + if (stat(mediadev.c_str(), &pstat)) + continue; + + ret = testMediaDevice(mediadev); + if (ret) + return ret; + + } + + return ret; +} + +TEST_REGISTER(MediaDeviceTest); diff --git a/test/media_device/meson.build b/test/media_device/meson.build new file mode 100644 index 0000000..b1d2115 --- /dev/null +++ b/test/media_device/meson.build @@ -0,0 +1,5 @@ + +media_device_test = executable('media_device_test', 'media_device_test.cpp', + link_with : [libcamera, libtest], + include_directories : [libcamera_internal_includes, + libtest_includes],) diff --git a/test/meson.build b/test/meson.build index a74629f..6233d2c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -3,6 +3,7 @@ libtest_sources = files([ ]) libtest = static_library('libtest', libtest_sources) +libtest_includes = include_directories('./') test_init = executable('test_init', 'init.cpp', link_with : libcamera, @@ -12,5 +13,8 @@ list = executable('list', 'list.cpp', link_with : [libcamera, libtest], include_directories : libcamera_includes) +subdir('media_device') + test('Initialisation test', test_init) test('List Camera API tests', list) +test('Media Device Test', media_device_test)