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',