Patch Detail
Show a patch.
GET /api/1.1/patches/64/?format=api
{ "id": 64, "url": "https://patchwork.libcamera.org/api/1.1/patches/64/?format=api", "web_url": "https://patchwork.libcamera.org/patch/64/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<1545324285-16730-3-git-send-email-jacopo@jmondi.org>", "date": "2018-12-20T16:44:45", "name": "[libcamera-devel,2/2] libcamera: Add MediaDevice class", "commit_ref": null, "pull_url": null, "state": "accepted", "archived": false, "hash": "9effc9ef2d6041a06d42576abb5d2199e0a31480", "submitter": { "id": 3, "url": "https://patchwork.libcamera.org/api/1.1/people/3/?format=api", "name": "Jacopo Mondi", "email": "jacopo@jmondi.org" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/64/mbox/", "series": [ { "id": 33, "url": "https://patchwork.libcamera.org/api/1.1/series/33/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=33", "date": "2018-12-20T16:44:43", "name": "Add MediaDevice and associated MediaObjects", "version": 1, "mbox": "https://patchwork.libcamera.org/series/33/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/64/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/64/checks/", "tags": {}, "headers": { "Return-Path": "<jacopo@jmondi.org>", "Received": [ "from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0B7D060B1F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 20 Dec 2018 17:45:09 +0100 (CET)", "from w540.lan (2-224-242-101.ip172.fastwebnet.it [2.224.242.101])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id 710FE240009;\n\tThu, 20 Dec 2018 16:45:08 +0000 (UTC)" ], "From": "Jacopo Mondi <jacopo@jmondi.org>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Thu, 20 Dec 2018 17:44:45 +0100", "Message-Id": "<1545324285-16730-3-git-send-email-jacopo@jmondi.org>", "X-Mailer": "git-send-email 2.7.4", "In-Reply-To": "<1545324285-16730-1-git-send-email-jacopo@jmondi.org>", "References": "<1545324285-16730-1-git-send-email-jacopo@jmondi.org>", "Subject": "[libcamera-devel] [PATCH 2/2] libcamera: Add MediaDevice class", "X-BeenThere": "libcamera-devel@lists.libcamera.org", "X-Mailman-Version": "2.1.23", "Precedence": "list", "List-Id": "<libcamera-devel.lists.libcamera.org>", "List-Unsubscribe": "<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>", "List-Archive": "<https://lists.libcamera.org/pipermail/libcamera-devel/>", "List-Post": "<mailto:libcamera-devel@lists.libcamera.org>", "List-Help": "<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>", "List-Subscribe": "<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>", "X-List-Received-Date": "Thu, 20 Dec 2018 16:45:09 -0000" }, "content": "The MediaDevice object implements handling and configuration of the media\ngraph associated with a V4L2 media device.\n\nThe class allows enumeration of all pads, links and entities registered in\nthe media graph, and provides methods to setup and reset media links.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n src/libcamera/include/media_device.h | 72 +++++\n src/libcamera/media_device.cpp | 604 +++++++++++++++++++++++++++++++++++\n src/libcamera/meson.build | 1 +\n 3 files changed, 677 insertions(+)\n create mode 100644 src/libcamera/include/media_device.h\n create mode 100644 src/libcamera/media_device.cpp", "diff": "diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h\nnew file mode 100644\nindex 0000000..3aa562a\n--- /dev/null\n+++ b/src/libcamera/include/media_device.h\n@@ -0,0 +1,72 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2018, Google Inc.\n+ *\n+ * media_device.h - Media device handler\n+ */\n+#ifndef __LIBCAMERA_MEDIA_DEVICE_H__\n+#define __LIBCAMERA_MEDIA_DEVICE_H__\n+\n+#include <map>\n+#include <string>\n+#include <sstream>\n+#include <vector>\n+\n+#include <linux/media.h>\n+\n+#include \"log.h\"\n+#include \"media_object.h\"\n+\n+namespace libcamera {\n+\n+class MediaDevice\n+{\n+public:\n+\tMediaDevice() : fd_(-1) { };\n+\t~MediaDevice();\n+\n+\tstd::string name() { return name_; }\n+\tstd::string path() { return path_; }\n+\n+\tint open(const std::string &path);\n+\tint close();\n+\tint enumerate(std::map<std::string, std::string> &entitiesMap);\n+\tvoid dumpGraph(std::ostream &os);\n+\n+\tint resetLinks();\n+\tint link(const std::string &source, unsigned int sourceIdx,\n+\t\t const std::string &sink, unsigned int sinkIdx,\n+\t\t unsigned int flags);\n+\n+\n+private:\n+\t/** The media device file descriptor */\n+\tint fd_;\n+\t/** The media device name as returned by MEDIA_IOC_DEVICE_INFO */\n+\tstd::string name_;\n+\t/** The media device path */\n+\tstd::string path_;\n+\n+\tstd::map<unsigned int, MediaObject *> mediaObjects_;\n+\tMediaObject *getObject(unsigned int id);\n+\tvoid addObject(MediaObject *obj);\n+\tvoid deleteObjects();\n+\n+\tstd::vector<MediaEntity *> entities_;\n+\tMediaEntity *getEntityByName(const std::string &name);\n+\n+\tint enumerateEntities(std::map<std::string, std::string> &entitiesMap,\n+\t\t\t struct media_v2_topology &topology);\n+\tint enumeratePads(struct media_v2_topology &topology);\n+\tint enumerateLinks(struct media_v2_topology &topology);\n+\n+\tint setupLink(MediaPad *source, MediaPad *sink,\n+\t\t MediaLink *link, unsigned int flags);\n+\n+\tvoid dumpLocal(MediaEntity *e, MediaPad *p, std::ostream &os);\n+\tvoid dumpRemote(MediaLink *l, std::ostream &os);\n+\tvoid dumpLink(MediaLink *l, std::ostream &os);\n+};\n+\n+} /* namespace libcamera */\n+#endif /* __LIBCAMERA_MEDIA_DEVICE_H__ */\ndiff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp\nnew file mode 100644\nindex 0000000..4b3dd2f\n--- /dev/null\n+++ b/src/libcamera/media_device.cpp\n@@ -0,0 +1,604 @@\n+/* SPDX-License-Identifier: LGPL-2.1-or-later */\n+/*\n+ * Copyright (C) 2018, Google Inc.\n+ *\n+ * media_device.cpp - Media device handler\n+ */\n+\n+#include <errno.h>\n+#include <fcntl.h>\n+#include <string.h>\n+#include <sys/ioctl.h>\n+#include <unistd.h>\n+\n+#include <string>\n+#include <vector>\n+\n+#include <linux/media.h>\n+\n+#include \"log.h\"\n+#include \"media_device.h\"\n+\n+/**\n+ * \\file media_device.h\n+ */\n+namespace libcamera {\n+\n+/**\n+ * \\class MediaDevice\n+ * \\brief Media device handler\n+ *\n+ * MediaDevice handles the media graph associated with a V4L2 media device.\n+ */\n+\n+/**\n+ * \\fn MediaDevice::~MediaDevice()\n+ * \\brief Close the media device file descriptor and release entities\n+ */\n+MediaDevice::~MediaDevice()\n+{\n+\tif (fd_ > -1)\n+\t\t::close(fd_);\n+\tdeleteObjects();\n+}\n+\n+/**\n+ * \\fn MediaDevice::name()\n+ * \\brief Return the media device name\n+ */\n+\n+/**\n+ * \\fn MediaDevice::path()\n+ * \\brief Return the media device path node associated with this MediaDevice\n+ */\n+\n+/**\n+ * \\fn MediaDevice::deleteObjects()\n+ * \\brief Delete all registered entities in the MediaDevice object\n+ */\n+void MediaDevice::deleteObjects()\n+{\n+\tfor (auto const &e : mediaObjects_)\n+\t\tdelete e.second;\n+\n+\tmediaObjects_.clear();\n+\tentities_.clear();\n+}\n+\n+/**\n+ * \\fn int MediaDevice::open(std::string)\n+ * \\brief Open a media device and initialize its components.\n+ * \\param path The media device path\n+ */\n+int MediaDevice::open(const std::string &path)\n+{\n+\tfd_ = ::open(path.c_str(), O_RDWR);\n+\tif (fd_ < 0) {\n+\t\tLOG(Error) << \"Failed to open media device at \" << path\n+\t\t\t << \": \" << strerror(errno);\n+\t\treturn -errno;\n+\t}\n+\tpath_ = path;\n+\n+\tstruct media_device_info info = { };\n+\tint ret = ioctl(fd_, MEDIA_IOC_DEVICE_INFO, &info);\n+\tif (ret) {\n+\t\tLOG(Error) << \"Failed to get media device info \"\n+\t\t\t << \": \" << strerror(errno);\n+\t\treturn -errno;\n+\t}\n+\n+\tname_ = info.model;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn MediaDevice::close()\n+ * \\brief Close the file descriptor associated with the media device\n+ */\n+int MediaDevice::close()\n+{\n+\tif (fd_ > -1)\n+\t\treturn ::close(fd_);\n+\n+\treturn 0;\n+}\n+\n+void MediaDevice::addObject(MediaObject *obj)\n+{\n+\n+\tif (mediaObjects_.find(obj->id()) != mediaObjects_.end()) {\n+\t\tLOG(Error) << \"Element with id \" << obj->id()\n+\t\t\t << \" already enumerated.\";\n+\t\treturn;\n+\t}\n+\n+\tmediaObjects_[obj->id()] = obj;\n+}\n+\n+MediaObject *MediaDevice::getObject(unsigned int id)\n+{\n+\tstd::map<unsigned int, MediaObject *>::iterator it =\n+\t\t\t\t\t\t\tmediaObjects_.find(id);\n+\treturn (it == mediaObjects_.end()) ?\n+\t nullptr : it->second;\n+}\n+\n+/**\n+ * \\fn MediaDevice::getEntityByName(std::string)\n+ * \\brief Return entity with name \\a name\n+ * \\param name The entity name\n+ */\n+MediaEntity *MediaDevice::getEntityByName(const std::string &name)\n+{\n+\tstd::vector<MediaEntity *>::iterator it = entities_.begin();\n+\n+\twhile (it != entities_.end()) {\n+\t\tMediaEntity *e = *it;\n+\t\tif (!(e->name().compare(name)))\n+\t\t\treturn e;\n+\t\tit++;\n+\t}\n+\n+\treturn nullptr;\n+}\n+\n+/**\n+ * \\fn MediaDevice::enumerateLinks(struct media_v2_topology &topology)\n+ * \\brief Enumerate all links in the system and associate them with their\n+ * source and sink pads\n+ * \\param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY\n+ */\n+int MediaDevice::enumerateLinks(struct media_v2_topology &topology)\n+{\n+\tstruct media_v2_link *link = reinterpret_cast<struct media_v2_link *>\n+\t\t\t\t (topology.ptr_links);\n+\n+\tfor (unsigned int i = 0; i < topology.num_links; i++, link++) {\n+\t\t/*\n+\t\t * Skip links between entities and interfaces: we only care\n+\t\t * about pad-2-pad links here.\n+\t\t */\n+\t\tif ((link->flags & MEDIA_LNK_FL_LINK_TYPE) ==\n+\t\t MEDIA_LNK_FL_INTERFACE_LINK)\n+\t\t\tcontinue;\n+\n+\t\tMediaLink *mediaLink = new MediaLink(link);\n+\t\taddObject(mediaLink);\n+\n+\t\t/* Store reference to this mediaLink in the link's source pad. */\n+\t\tMediaPad *mediaPad = dynamic_cast<MediaPad *>\n+\t\t\t\t (getObject(mediaLink->source()));\n+\t\tif (mediaPad == nullptr) {\n+\t\t\tLOG(Error) << \"Failed to find pad with id: \"\n+\t\t\t\t << mediaLink->source();\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t\tmediaPad->addLink(mediaLink);\n+\n+\t\t/* Store reference to this mediaLink in the link's sink pad. */\n+\t\tmediaPad = dynamic_cast<MediaPad *>(getObject(mediaLink->sink()));\n+\t\tif (mediaPad == nullptr) {\n+\t\t\tLOG(Error) << \"Failed to find pad with id: \"\n+\t\t\t\t << mediaLink->sink();\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\t\tmediaPad->addLink(mediaLink);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn MediaDevice::enumeratePads(struct media_v2_topology &topology)\n+ * \\brief Enumerate all pads in the system and associate them with the\n+ * entity they belong to\n+ * \\param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY\n+ */\n+int MediaDevice::enumeratePads(struct media_v2_topology &topology)\n+{\n+\tstruct media_v2_pad *pad = reinterpret_cast<struct media_v2_pad *>\n+\t\t\t\t (topology.ptr_pads);\n+\n+\tfor (unsigned int i = 0; i < topology.num_pads; i++, pad++) {\n+\t\tMediaPad *mediaPad = new MediaPad(pad);\n+\t\taddObject(mediaPad);\n+\n+\t\t/* Store a reference to this MediaPad in pad's entity. */\n+\t\tMediaEntity *mediaEntity = dynamic_cast<MediaEntity *>\n+\t\t\t\t\t (getObject(mediaPad->entity()));\n+\t\tif (mediaEntity == nullptr) {\n+\t\t\tLOG(Error) << \"Failed to find entity with id: \"\n+\t\t\t\t << mediaPad->entity();\n+\t\t\treturn -ENODEV;\n+\t\t}\n+\n+\t\tmediaEntity->addPad(mediaPad);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn MediaDevice::enumerateEntities(std::map<std::string, std::string> &,\n+ * struct media_v2_topology &topology)\n+ * \\brief Enumerate and initialize entities in the media graph\n+ * \\param entitiesMap Map entities names to their video (sub)device node\n+ * \\param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY\n+ *\n+ * Associate the video (sub)device path to the entity name as returned by\n+ * MEDIA_IOC_G_TOPOLOGY\n+ */\n+int MediaDevice::enumerateEntities(std::map<std::string, std::string> &entitiesMap,\n+\t\t\t\t struct media_v2_topology &topology)\n+{\n+\tstruct media_v2_entity *entities =\n+\t\t\treinterpret_cast<struct media_v2_entity *>\n+\t\t\t(topology.ptr_entities);\n+\n+\tfor (unsigned int i = 0; i < topology.num_entities; ++i) {\n+\t\tstd::map<std::string, std::string>::iterator it;\n+\n+\t\tit = entitiesMap.find(entities[i].name);\n+\t\tif (it == entitiesMap.end()) {\n+\t\t\tLOG(Error) << \"Entity \" << entities[i].name\n+\t\t\t\t << \" not found in media entities map\";\n+\t\t\treturn -ENOENT;\n+\t\t}\n+\n+\t\tMediaEntity *entity = new MediaEntity(&entities[i]);\n+\t\tif (entity->setDevice(it->second)) {\n+\t\t\tdelete entity;\n+\t\t\tgoto delete_entities;\n+\t\t}\n+\n+\t\taddObject(entity);\n+\t\tentities_.push_back(entity);\n+\t}\n+\n+\treturn 0;\n+\n+delete_entities:\n+\tdeleteObjects();\n+\n+\treturn -errno;\n+}\n+\n+/**\n+ * \\fn MediaDevice::enumerate(std::map<std::string, std::string>)\n+ * \\brief Enumerate the media graph topology\n+ * \\param entitiesMap Map entities names to their video (sub)device node\n+ * FIXME: this is statically provided by the caller at the moment.\n+ *\n+ * This functions enumerates all media objects, registered in the media graph,\n+ * through the MEDIA_IOC_G_TOPOLOGY ioctl. For each returned entity,\n+ * it creates and store its representation for later reuse.\n+ */\n+int MediaDevice::enumerate(std::map<std::string, std::string> &entitiesMap)\n+{\n+\tstruct media_v2_topology topology = { };\n+\tunsigned int num_interfaces;\n+\tunsigned int num_links;\n+\tunsigned int num_pads;\n+\tunsigned int num_ent;\n+\n+\tdo {\n+\t\tnum_ent = topology.num_entities;\n+\t\tnum_pads = topology.num_pads;\n+\t\tnum_links = topology.num_links;\n+\t\tnum_interfaces = topology.num_interfaces;\n+\n+\t\t/* Call G_TOPOLOGY the first time here to enumerate .*/\n+\t\tif (ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology)) {\n+\t\t\tLOG(Error) << \"Failed to enumerate media topology on\"\n+\t\t\t\t << path_ << \": \" << strerror(errno);\n+\t\t\treturn -errno;\n+\t\t}\n+\n+\t\t/*\n+\t\t * Repeat the call until we don't get a 'stable' number\n+\t\t * of media objects.\n+\t\t */\n+\t} while (num_ent != topology.num_entities ||\n+\t\t num_pads != topology.num_pads ||\n+\t\t num_links != topology.num_links ||\n+\t\t num_interfaces != topology.num_interfaces);\n+\n+\tstruct media_v2_entity *_ptr_e =\n+\t\t\t\tnew struct media_v2_entity[topology.num_entities];\n+\ttopology.ptr_entities = reinterpret_cast<__u64>(_ptr_e);\n+\n+\tstruct media_v2_pad *_ptr_p =\n+\t\t\t\tnew struct media_v2_pad[topology.num_pads];\n+\ttopology.ptr_pads = reinterpret_cast<__u64>(_ptr_p);\n+\n+\tstruct media_v2_link *_ptr_l =\n+\t\t\t\tnew struct media_v2_link[topology.num_links];\n+\ttopology.ptr_links = reinterpret_cast<__u64>(_ptr_l);\n+\n+\t/* Call G_TOPOLOGY again, this time with memory reserved. */\n+\tint ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology);\n+\tif (ret < 0) {\n+\t\tLOG(Error) << \"Failed to enumerate media topology on \" << path_\n+\t\t\t << \": \" << strerror(errno);\n+\t\tret = -errno;\n+\t\tgoto error_free_mem;\n+\t}\n+\n+\tret = enumerateEntities(entitiesMap, topology);\n+\tif (ret)\n+\t\tgoto error_free_mem;\n+\n+\tret = enumeratePads(topology);\n+\tif (ret)\n+\t\tgoto error_free_objs;\n+\n+\tret = enumerateLinks(topology);\n+\tif (ret)\n+\t\tgoto error_free_objs;\n+\n+\tdelete[] _ptr_e;\n+\tdelete[] _ptr_p;\n+\tdelete[] _ptr_l;\n+\n+\treturn 0;\n+\n+error_free_objs:\n+\tdeleteObjects();\n+\n+error_free_mem:\n+\tdelete[] _ptr_e;\n+\tdelete[] _ptr_p;\n+\tdelete[] _ptr_l;\n+\n+\treturn ret;\n+}\n+\n+void MediaDevice::dumpLocal(MediaEntity *e, MediaPad *p, std::ostream &os)\n+{\n+\tos << \"\\t \\\"\" << e->name() << \"\\\"[\"\n+\t << p->index() << \"]\";\n+}\n+\n+void MediaDevice::dumpRemote(MediaLink *l, std::ostream &os)\n+{\n+\n+\tMediaPad *remotePad = dynamic_cast<MediaPad *>\n+\t\t\t (getObject(l->sink()));\n+\tif (remotePad == nullptr)\n+\t\treturn;\n+\n+\tMediaEntity *remoteEntity =\n+\t\t\tdynamic_cast<MediaEntity *>\n+\t\t\t(getObject(remotePad->entity()));\n+\tif (remoteEntity == nullptr)\n+\t\treturn;\n+\n+\tos << \"\\\"\" << remoteEntity->name() << \"\\\"[\"\n+\t << remotePad->index() << \"]\";\n+}\n+\n+void MediaDevice::dumpLink(MediaLink *l, std::ostream &os)\n+{\n+\tunsigned int flags = l->flags();\n+\n+\tos << \" [\";\n+\tif (flags) {\n+\t\tos << (flags & MEDIA_LNK_FL_ENABLED ? \"ENABLED,\" : \"\")\n+\t\t << (flags & MEDIA_LNK_FL_IMMUTABLE ? \"IMMUTABLE\" : \"\");\n+\t}\n+\tos << \"]\\n\";\n+}\n+\n+/**\n+ * \\fn MediaDevice::dumpGraph(std::ostream)\n+ * \\brief Dump the media device topology in textual form to an output stream\n+ * \\param os The output stream where to append the printed topology to\n+ */\n+void MediaDevice::dumpGraph(std::ostream &os)\n+{\n+\tos << \"\\n\" << name_ << \" - \" << path_ << \"\\n\\n\";\n+\n+\tfor (auto const &e : entities_) {\n+\t\tos << \"\\\"\" << e->name() << \"\\\"\\n\";\n+\n+\t\tfor (auto const &p : e->sinks()) {\n+\t\t\tos << \" [\" << p->index() << \"]\" << \": Sink\\n\";\n+\t\t\tfor (auto const &l : p->links()) {\n+\t\t\t\tdumpLocal(e, p, os);\n+\t\t\t\tos << \" <- \";\n+\t\t\t\tdumpRemote(l, os);\n+\t\t\t\tdumpLink(l, os);\n+\t\t\t}\n+\t\t\tos << \"\\n\";\n+\t\t}\n+\n+\t\tfor (auto const &p : e->sources()) {\n+\t\t\tos << \" [\" << p->index() << \"]\" << \": Source\\n\";\n+\t\t\tfor (auto const &l : p->links()) {\n+\t\t\t\tdumpLocal(e, p, os);\n+\t\t\t\tos << \" -> \";\n+\t\t\t\tdumpRemote(l, os);\n+\t\t\t\tdumpLink(l, os);\n+\t\t\t}\n+\t\t\tos << \"\\n\";\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * \\fn MediaDevice::setupLink(MediaPad *source, MediaPad *sink)\n+ * \\brief Apply \\a flags to the link between \\a source and \\a sink pads\n+ * \\param source The source MediaPad\n+ * \\param sink The sink MediaPad\n+ * \\param link The MediaLink to operate on\n+ * \\param flags Flags to be applied to the link (MEDIA_LNK_FL_*)\n+ */\n+int MediaDevice::setupLink(MediaPad *source, MediaPad *sink,\n+\t\t\t MediaLink *link, unsigned int flags)\n+{\n+\tstruct media_link_desc linkDesc = { };\n+\n+\tlinkDesc.source.entity = source->entity();\n+\tlinkDesc.source.index = source->index();\n+\tlinkDesc.source.flags = MEDIA_PAD_FL_SOURCE;\n+\n+\tlinkDesc.sink.entity = sink->entity();\n+\tlinkDesc.sink.index = sink->index();\n+\tlinkDesc.sink.flags = MEDIA_PAD_FL_SINK;\n+\n+\tlinkDesc.flags = flags;\n+\n+\tif (ioctl(fd_, MEDIA_IOC_SETUP_LINK, &linkDesc)) {\n+\t\tLOG(Error) << \"Failed to setup link: \"\n+\t\t\t << strerror(errno);\n+\t\treturn -errno;\n+\t}\n+\n+\tlink->setFlags(0);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn MediaDevice::resetLinks()\n+ * \\brief Reset all links on the media graph\n+ *\n+ * Walk all registered entities, and disable all links from their\n+ * source pads to other pads.\n+ */\n+int MediaDevice::resetLinks()\n+{\n+\tfor (MediaEntity *e : entities_) {\n+\t\tfor (MediaPad *sourcePad : e->sources()) {\n+\t\t\tfor (MediaLink *l : sourcePad->links()) {\n+\t\t\t\t/*\n+\t\t\t\t * Do not reset links that are not enabled\n+\t\t\t\t * or immutable.\n+\t\t\t\t */\n+\t\t\t\tif (l->flags() & MEDIA_LNK_FL_IMMUTABLE)\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\tif (!(l->flags() & MEDIA_LNK_FL_ENABLED))\n+\t\t\t\t\tcontinue;\n+\n+\t\t\t\t/* Get the remote sink pad. */\n+\t\t\t\tMediaPad *sinkPad = dynamic_cast<MediaPad *>\n+\t\t\t\t\t\t (getObject(l->sink()));\n+\t\t\t\tif (sinkPad == nullptr)\n+\t\t\t\t\treturn -ENOENT;\n+\n+\t\t\t\t/* Also get entity to make sure IDs are ok. */\n+\t\t\t\tMediaEntity *sinkEntity =\n+\t\t\t\t\t\tdynamic_cast<MediaEntity *>\n+\t\t\t\t\t\t(getObject(sinkPad->entity()));\n+\t\t\t\tif (sinkEntity == nullptr)\n+\t\t\t\t\treturn -ENOENT;\n+\n+\t\t\t\tint ret = setupLink(sourcePad, sinkPad, l, 0);\n+\t\t\t\tif (ret) {\n+\t\t\t\t\tLOG(Error) << \"Link reset failed: \"\n+\t\t\t\t\t\t << e->name() << \"[\"\n+\t\t\t\t\t\t << sourcePad->index()\n+\t\t\t\t\t\t << \"] -> \"\n+\t\t\t\t\t\t << sinkEntity->name() << \"[\"\n+\t\t\t\t\t\t << sinkPad->index() << \"]\";\n+\t\t\t\t\treturn ret;\n+\t\t\t\t}\n+\n+\t\t\t\tLOG(Info) << \"Link reset: \"\n+\t\t\t\t\t << e->name() << \"[\"\n+\t\t\t\t\t << sourcePad->index()\n+\t\t\t\t\t << \"] -> \"\n+\t\t\t\t\t << sinkEntity->name() << \"[\"\n+\t\t\t\t\t << sinkPad->index() << \"]\";\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\fn MediaDevice::link(std::string, unsigned int, std::string, unsigned int)\n+ * \\brief Setup a link identified by the entities name and their source and\n+ * sink pad indexes\n+ * \\param source The source entity name\n+ * \\param sourceIdx The source pad index\n+ * \\param sink The sink entity name\n+ * \\param sinkIdx The sink pad index\n+ * \\param flags The link setup flag (see MEDIA_LNK_FL_*)\n+ */\n+int MediaDevice::link(const std::string &source, unsigned int sourceIdx,\n+\t\t const std::string &sink, unsigned int sinkIdx,\n+\t\t unsigned int flags)\n+{\n+\n+\t/* Make sure the supplied link is well formed. */\n+\tMediaEntity *sourceEntity = getEntityByName(source);\n+\tif (sourceEntity == nullptr) {\n+\t\tLOG(Error) << \"Entity name: \" << source << \"not found\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tMediaEntity *sinkEntity = getEntityByName(sink);\n+\tif (sinkEntity == nullptr) {\n+\t\tLOG(Error) << \"Entity name: \" << source << \"not found\";\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tMediaPad *sourcePad = sourceEntity->getPadByIndex(sourceIdx);\n+\tif (sourcePad == nullptr) {\n+\t\tLOG(Error) << \"Pad \" << sourceIdx << \"not found in entity \"\n+\t\t\t << sourceEntity->name();\n+\t\treturn -ENOENT;\n+\t}\n+\n+\tMediaPad *sinkPad = sinkEntity->getPadByIndex(sinkIdx);\n+\tif (sinkPad == nullptr) {\n+\t\tLOG(Error) << \"Pad \" << sinkIdx << \"not found in entity \"\n+\t\t\t << sinkEntity->name();\n+\t\treturn -ENOENT;\n+\t}\n+\n+\t/*\n+\t * Walk all links in the source and search for an entry matching the\n+\t * pad ids. If none, the requested link does not exists.\n+\t */\n+\tMediaLink *validLink = nullptr;\n+\tfor (MediaLink *link : sourcePad->links()) {\n+\t\tif (link->source() != sourcePad->id())\n+\t\t\tcontinue;\n+\n+\t\tif (link->sink() != sinkPad->id())\n+\t\t\tcontinue;\n+\n+\t\tvalidLink = link;\n+\t\tbreak;\n+\t}\n+\n+\tif (validLink == nullptr) {\n+\t\tLOG(Error) << \"Link not found\"\n+\t\t\t << \"\\\"\" << sourceEntity->name() << \"\\\"[\"\n+\t\t\t << sourcePad->index() << \"] -> \"\n+\t\t\t << \"\\\"\" << sinkEntity->name() << \"\\\"[\"\n+\t\t\t << sinkPad->index() << \"]\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tint ret = setupLink(sourcePad, sinkPad, validLink, flags);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tLOG(Info) << \"Setup link: \"\n+\t\t << \"\\\"\" << sourceEntity->name() << \"\\\"[\"\n+\t\t << sourcePad->index() << \"] -> \"\n+\t\t << \"\\\"\" << sinkEntity->name() << \"\\\"[\"\n+\t\t << sinkPad->index() << \"]\"\n+\t\t << \" [\" << flags << \"]\";\n+\n+\treturn 0;\n+}\n+\n+} /* namespace libcamera */\ndiff --git a/src/libcamera/meson.build b/src/libcamera/meson.build\nindex da06eba..4cac687 100644\n--- a/src/libcamera/meson.build\n+++ b/src/libcamera/meson.build\n@@ -2,6 +2,7 @@ libcamera_sources = files([\n 'log.cpp',\n 'main.cpp',\n 'media_object.cpp',\n+ 'media_device.cpp',\n ])\n \n libcamera_headers = files([\n", "prefixes": [ "libcamera-devel", "2/2" ] }