From patchwork Wed Jan 2 00:49:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 127 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3462160B30 for ; Wed, 2 Jan 2019 01:48:08 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A9F7F505 for ; Wed, 2 Jan 2019 01:48:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546390087; bh=Ht6R9xbDXuATQK4BdllJKlU5jcPbz9AZP0pXp0qK+JU=; h=From:To:Subject:Date:From; b=jU3y16oafU4TPbKi+FA5MN5p1g9cPbqAVMnU2RKSGVkEfwjuDvx/pIkoLEWDDziwX Grpjh6tjDv/JaofOth6ijCFSgCNOTErmLL536wqbbHEvIaobZMewfDVwdkdJTuMEqU TsuRELrMtRJB22AioFik6s7yKBWO0X/YNkRfI+Oc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 2 Jan 2019 02:49:01 +0200 Message-Id: <20190102004903.24190-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 1/3] libcamera: media_device: Add DeviceInfo features 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: Wed, 02 Jan 2019 00:48:08 -0000 From: Jacopo Mondi Add the features of the DeviceInfo class needed to replace it with MediaDevice. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart --- src/libcamera/include/media_device.h | 5 +++ src/libcamera/media_device.cpp | 52 ++++++++++++++++++++++++++-- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h index d787be391882..3fcdb4b4d5f8 100644 --- a/src/libcamera/include/media_device.h +++ b/src/libcamera/include/media_device.h @@ -24,6 +24,10 @@ public: MediaDevice(const std::string &devnode); ~MediaDevice(); + bool acquire(); + void release() { acquired_ = false; } + bool busy() const { return acquired_; } + int open(); void close(); @@ -41,6 +45,7 @@ private: std::string devnode_; int fd_; bool valid_; + bool acquired_; std::map objects_; MediaObject *object(unsigned int id); diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 1bd4f43a0c79..605e504be124 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -52,7 +52,11 @@ namespace libcamera { * controller device node. It can be closed at any time with a call to close(). * This will not invalidate the media graph and all cached media objects remain * valid and can be accessed normally. The device can then be later reopened if - * needed to perform other operations that interact with the device node. + * needed to perform other operations that interect with the device node. + * + * Media device can be claimed for exclusive use with acquire(), released with + * release() and tested with busy(). This mechanism is aimed at pipeline + * managers to claim media devices they support during enumeration. */ /** @@ -63,7 +67,7 @@ namespace libcamera { * populated with open() and populate() before the media graph can be queried. */ MediaDevice::MediaDevice(const std::string &devnode) - : devnode_(devnode), fd_(-1), valid_(false) + : devnode_(devnode), fd_(-1), valid_(false), acquired_(false) { } @@ -74,6 +78,50 @@ MediaDevice::~MediaDevice() clear(); } +/** + * \brief Claim a device for exclusive use + * + * The device claiming mechanism offers simple media device access arbitration + * between multiple users. When the media device is created, it is available to + * all users. Users can query the media graph to determine whether they can + * support the device and, if they do, claim the device for exclusive use. Other + * users are then expected to skip over media devices in use as reported by the + * busy() function. + * + * Once claimed the device shall be released by its user when not needed anymore + * by calling the release() function. + * + * Exclusive access is only guaranteed if all users of the media device abide by + * the device claiming mechanism, as it isn't enforced by the media device + * itself. + * + * \return true if the device was successfully claimed, or false if it was + * already in use + * \sa release(), busy() + */ +bool MediaDevice::acquire() +{ + if (acquired_) + return false; + + acquired_ = true; + return true; +} + +/** + * \fn MediaDevice::release() + * \brief Release a device previously claimed for exclusive use + * \sa acquire(), busy() + */ + +/** + * \fn MediaDevice::busy() + * \brief Check if a device is in use + * \return true if the device has been claimed for exclusive user, or false if + * it is available + * \sa acquire(), release() + */ + /** * \brief Open a media device and retrieve device information * From patchwork Wed Jan 2 00:49:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 128 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7B3C560B30 for ; Wed, 2 Jan 2019 01:48:08 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 07C5C537 for ; Wed, 2 Jan 2019 01:48:07 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546390088; bh=e8hkoztoWKXaxDWSisshkyI0Or3dA00sgdccn5X+DtE=; h=From:To:Subject:Date:In-Reply-To:References:From; b=KDx7UfVak1W3RasTW97mcExM8rMDWRMy6qd9uilkLgmu4l+QI/4emlenwOcQu5li7 CjMpnBHdk5ev2HvLwW+4XZ3PjWlYNqYuE8G866rvs7+8ebb3w/3RVglJyoBu5fL7Rw ZAnGNLtRmcWAsnlRKZf0VPQ/Cm9iexy57v9fWuGw= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 2 Jan 2019 02:49:02 +0200 Message-Id: <20190102004903.24190-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190102004903.24190-1-laurent.pinchart@ideasonboard.com> References: <20190102004903.24190-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/3] libcamera: MediaDevice: Create entities with major and minor numbers 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: Wed, 02 Jan 2019 00:48:08 -0000 From: Jacopo Mondi Extend the MediaEntity object with device node major and minor numbers, and retrieve them from the media graph using interfaces. They will be used by the DeviceEnumerator to retrieve the devnode path. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart --- src/libcamera/include/media_device.h | 2 + src/libcamera/include/media_object.h | 9 +++- src/libcamera/media_device.cpp | 65 +++++++++++++++++++++++++++- src/libcamera/media_object.cpp | 46 ++++++++++++++++++-- 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/src/libcamera/include/media_device.h b/src/libcamera/include/media_device.h index 3fcdb4b4d5f8..8d491a87867c 100644 --- a/src/libcamera/include/media_device.h +++ b/src/libcamera/include/media_device.h @@ -54,6 +54,8 @@ private: std::vector entities_; + struct media_v2_interface *findInterface(const struct media_v2_topology &topology, + unsigned int entityId); bool populateEntities(const struct media_v2_topology &topology); bool populatePads(const struct media_v2_topology &topology); bool populateLinks(const struct media_v2_topology &topology); diff --git a/src/libcamera/include/media_object.h b/src/libcamera/include/media_object.h index 65b55085a3b0..950a33286690 100644 --- a/src/libcamera/include/media_object.h +++ b/src/libcamera/include/media_object.h @@ -80,21 +80,28 @@ class MediaEntity : public MediaObject { public: const std::string &name() const { return name_; } + unsigned int major() const { return major_; } + unsigned int minor() const { return minor_; } const std::vector &pads() const { return pads_; } const MediaPad *getPadByIndex(unsigned int index) const; const MediaPad *getPadById(unsigned int id) const; + int setDeviceNode(const std::string &devnode); + private: friend class MediaDevice; - MediaEntity(const struct media_v2_entity *entity); + MediaEntity(const struct media_v2_entity *entity, + unsigned int major = 0, unsigned int minor = 0); MediaEntity(const MediaEntity &) = delete; ~MediaEntity(); std::string name_; std::string devnode_; + unsigned int major_; + unsigned int minor_; std::vector pads_; diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 605e504be124..70b3fff3f492 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -208,6 +208,7 @@ int MediaDevice::populate() struct media_v2_entity *ents = nullptr; struct media_v2_link *links = nullptr; struct media_v2_pad *pads = nullptr; + struct media_v2_interface *interfaces = nullptr; __u64 version = -1; int ret; @@ -221,6 +222,7 @@ int MediaDevice::populate() topology.ptr_entities = reinterpret_cast<__u64>(ents); topology.ptr_links = reinterpret_cast<__u64>(links); topology.ptr_pads = reinterpret_cast<__u64>(pads); + topology.ptr_interfaces = reinterpret_cast<__u64>(interfaces); ret = ioctl(fd_, MEDIA_IOC_G_TOPOLOGY, &topology); if (ret < 0) { @@ -236,10 +238,12 @@ int MediaDevice::populate() delete[] links; delete[] ents; delete[] pads; + delete[] interfaces; ents = new media_v2_entity[topology.num_entities]; links = new media_v2_link[topology.num_links]; pads = new media_v2_pad[topology.num_pads]; + interfaces = new media_v2_interface[topology.num_interfaces]; version = topology.topology_version; } @@ -253,6 +257,7 @@ int MediaDevice::populate() delete[] links; delete[] ents; delete[] pads; + delete[] interfaces; if (!valid_) { clear(); @@ -367,6 +372,45 @@ void MediaDevice::clear() * \brief Global list of media entities in the media graph */ +/** + * \brief Find the interface associated with an entity + * \param topology The media topology as returned by MEDIA_IOC_G_TOPOLOGY + * \param entityId The entity id + * \return A pointer to the interface if found, or nullptr otherwise + */ +struct media_v2_interface *MediaDevice::findInterface(const struct media_v2_topology &topology, + unsigned int entityId) +{ + struct media_v2_link *links = reinterpret_cast + (topology.ptr_links); + unsigned int ifaceId = -1; + + for (unsigned int i = 0; i < topology.num_links; ++i) { + /* Search for the interface to entity link. */ + if (links[i].sink_id != entityId) + continue; + + if ((links[i].flags & MEDIA_LNK_FL_LINK_TYPE) != + MEDIA_LNK_FL_INTERFACE_LINK) + continue; + + ifaceId = links[i].source_id; + } + + if (ifaceId == static_cast(-1)) + return nullptr; + + struct media_v2_interface *ifaces = reinterpret_cast + (topology.ptr_interfaces); + + for (unsigned int i = 0; i < topology.num_interfaces; ++i) { + if (ifaces[i].id == ifaceId) + return &ifaces[i]; + } + + return nullptr; +} + /* * For each entity in the media graph create a MediaEntity and store a * reference in the media device objects map and entities list. @@ -377,7 +421,26 @@ bool MediaDevice::populateEntities(const struct media_v2_topology &topology) (topology.ptr_entities); for (unsigned int i = 0; i < topology.num_entities; ++i) { - MediaEntity *entity = new MediaEntity(&mediaEntities[i]); + /* + * Find the interface linked to this entity to get the device + * node major and minor numbers. + */ + struct media_v2_interface *iface = + findInterface(topology, mediaEntities[i].id); + if (!iface) { + LOG(Error) << "Failed to find interface link for " + << "entity with id: " << mediaEntities[i].id; + return false; + } + + MediaEntity *entity; + if (iface) + entity = new MediaEntity(&mediaEntities[i], + iface->devnode.major, + iface->devnode.minor); + else + entity = new MediaEntity(&mediaEntities[i]); + if (!addObject(entity)) { delete entity; return false; diff --git a/src/libcamera/media_object.cpp b/src/libcamera/media_object.cpp index b64dcc3c8fb4..69e5cc74264d 100644 --- a/src/libcamera/media_object.cpp +++ b/src/libcamera/media_object.cpp @@ -6,9 +6,8 @@ */ #include -#include #include -#include +#include #include #include @@ -212,6 +211,20 @@ void MediaPad::addLink(MediaLink *link) * \return The entity name */ +/** + * \fn MediaEntity::major() + * \brief Retrieve the major number of the interface associated with the entity + * \return The interface major number, or 0 if the entity isn't associated with + * an interface + */ + +/** + * \fn MediaEntity::minor() + * \brief Retrieve the minor number of the interface associated with the entity + * \return The interface minor number, or 0 if the entity isn't associated with + * an interface + */ + /** * \fn MediaEntity::pads() * \brief Retrieve all pads of the entity @@ -238,6 +251,7 @@ const MediaPad *MediaEntity::getPadByIndex(unsigned int index) const * \param id The pad id * \return The pad identified by \a id, or nullptr if no such pad exist */ + const MediaPad *MediaEntity::getPadById(unsigned int id) const { for (MediaPad *p : pads_) { @@ -248,12 +262,36 @@ const MediaPad *MediaEntity::getPadById(unsigned int id) const return nullptr; } +/** + * \brief Set the path to the device node for the associated interface + * \param devnode The interface device node path associated with this entity + * \return 0 on success, or a negative error code if the device node can't be + * accessed + */ +int MediaEntity::setDeviceNode(const std::string &devnode) +{ + /* Make sure the device node can be accessed. */ + int ret = ::access(devnode.c_str(), R_OK | W_OK); + if (ret < 0) { + ret = -errno; + LOG(Error) << "Device node " << devnode << " can't be accessed: " + << strerror(-ret); + return ret; + } + + return 0; +} + /** * \brief Construct a MediaEntity * \param entity The media entity kernel data + * \param major The major number of the entity associated interface + * \param minor The minor number of the entity associated interface */ -MediaEntity::MediaEntity(const struct media_v2_entity *entity) - : MediaObject(entity->id), name_(entity->name) +MediaEntity::MediaEntity(const struct media_v2_entity *entity, + unsigned int major, unsigned int minor) + : MediaObject(entity->id), name_(entity->name), + major_(major), minor_(minor) { } From patchwork Wed Jan 2 00:49:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 129 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 024B160B30 for ; Wed, 2 Jan 2019 01:48:09 +0100 (CET) Received: from avalon.bb.dnainternet.fi (dfj612ybrt5fhg77mgycy-3.rev.dnainternet.fi [IPv6:2001:14ba:21f5:5b00:2e86:4862:ef6a:2804]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 57564505 for ; Wed, 2 Jan 2019 01:48:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1546390088; bh=JCv/CXK7OO2ZNTfYo8WanUC1fqo8QVhawn1tOKZoSZc=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HBdGmetstCehblNe1FtMPifu1Ahaq9yyoa3mG2yokBjukqBGNY2lUjPiagT0HubUw E+u0Uyqjr7jsKPtyBkeTljfdmaIGVejVQH9SlUQrS0ofuNLD6y0Epc34pa8heDwhex NXGk22Nl0Crqz5bBzebsGpJqFDe1AaJ43tjPTA4o= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Wed, 2 Jan 2019 02:49:03 +0200 Message-Id: <20190102004903.24190-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.19.2 In-Reply-To: <20190102004903.24190-1-laurent.pinchart@ideasonboard.com> References: <20190102004903.24190-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 3/3] libcamera: device_enumerator: Use MediaDevice 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: Wed, 02 Jan 2019 00:48:09 -0000 From: Jacopo Mondi Replace usage of the DeviceInfo class with MediaDevice in the DeviceEnumerator and remove the DeviceInfo class. Signed-off-by: Jacopo Mondi Signed-off-by: Laurent Pinchart --- src/libcamera/device_enumerator.cpp | 313 +++------------------- src/libcamera/include/device_enumerator.h | 33 +-- src/libcamera/pipeline/vimc.cpp | 17 +- 3 files changed, 50 insertions(+), 313 deletions(-) diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index d5ba869f3457..7eef450e0c65 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -13,6 +13,7 @@ #include "device_enumerator.h" #include "log.h" +#include "media_device.h" /** * \file device_enumerator.h @@ -28,8 +29,8 @@ * for other parts of libcamera. * * The DeviceEnumerator can enumerate all or specific media devices in - * the system. When a new media device is added the enumerator gathers - * information about it and stores it in a DeviceInfo object. + * the system. When a new media device is added the enumerator creates a + * corresponding MediaDevice instance. * * The last functionality provided is the ability to search among the * enumerate media devices for one matching information known to the @@ -42,135 +43,6 @@ namespace libcamera { -/** - * \class DeviceInfo - * \brief Container of information for enumerated device - * - * The DeviceInfo class holds information about a media device. It provides - * methods to retrieve the information stored and to lookup entity names - * to device node paths. Furthermore it provides a scheme where a device - * can be acquired and released to indicate if the device is in use. - * - * \todo Look into the possibility to replace this with a more complete MediaDevice model. - */ - -/** - * \brief Construct a container of device information - * - * \param[in] devnode The path to the device node of the media device - * \param[in] info Information retrieved from MEDIA_IOC_DEVICE_INFO IOCTL - * \param[in] entities A map of media graph 'Entity name' -> 'devnode path' - * - * The caller is responsible to provide all information for the device. - */ -DeviceInfo::DeviceInfo(const std::string &devnode, const struct media_device_info &info, - const std::map &entities) - : acquired_(false), devnode_(devnode), info_(info), entities_(entities) -{ - for (const auto &entity : entities_) - LOG(Info) << "Device: " << devnode_ << " Entity: '" << entity.first << "' -> " << entity.second; -} - -/** - * \brief Claim a device for exclusive use - * - * Once a device is successfully acquired the caller is responsible to - * release it once it is done wit it. - * - * \retval 0 Device claimed - * \retval -EBUSY Device already claimed by someone else - */ -int DeviceInfo::acquire() -{ - if (acquired_) - return -EBUSY; - - acquired_ = true; - - return 0; -} - -/** - * \brief Release a device from exclusive use - */ -void DeviceInfo::release() -{ - acquired_ = false; -} - -/** - * \brief Check if a device is in use - * - * \retval true Device is in use - * \retval false Device is free - */ -bool DeviceInfo::busy() const -{ - return acquired_; -} - -/** - * \brief Retrieve the devnode to the media device - * - * \return Path to the media device (example /dev/media0) - */ -const std::string &DeviceInfo::devnode() const -{ - return devnode_; -} - -/** - * \brief Retrieve the media device v4l2 information - * - * \return v4l2 specific information structure - */ -const struct media_device_info &DeviceInfo::info() const -{ - return info_; -} - -/** - * \brief List all entities of the device - * - * List all media entities names from the media graph which are known - * and to which this instance can lookup the device node path. - * - * \return List of strings - */ -std::vector DeviceInfo::entities() const -{ - std::vector entities; - - for (const auto &entity : entities_) - entities.push_back(entity.first); - - return entities; -} - -/** - * \brief Lookup a media entity name and retrieve its device node path - * - * \param[in] name Entity name to lookup - * \param[out] devnode Path to \a name devnode if lookup is successful - * - * The caller is responsible to check the return code of the function - * to determine if the entity name could be looked up. - * - * \return 0 on success none zero otherwise - */ -int DeviceInfo::lookup(const std::string &name, std::string &devnode) const -{ - auto it = entities_.find(name); - - if (it == entities_.end()) { - LOG(Error) << "Trying to lookup entity '" << name << "' which does not exist"; - return -ENODEV; - } - - devnode = it->second; - return 0; -} - /** * \class DeviceMatch * \brief Description of a media device search pattern @@ -205,25 +77,23 @@ void DeviceMatch::add(const std::string &entity) /** * \brief Compare a search pattern with a media device - * - * \param[in] info Information about a enumerated media device + * \param[in] device The media device * * Matching is performed on the Linux device driver name and entity names * from the media graph. * - * \retval true The device described in \a info matches search pattern - * \retval false The device described in \a info do not match search pattern + * \return true if the media device matches the search pattern, false otherwise */ -bool DeviceMatch::match(const DeviceInfo *info) const +bool DeviceMatch::match(const MediaDevice *device) const { - if (driver_ != info->info().driver) + if (driver_ != device->driver()) return false; for (const std::string &name : entities_) { bool found = false; - for (const std::string &entity : info->entities()) { - if (name == entity) { + for (const MediaEntity *entity : device->entities()) { + if (name == entity->name()) { found = true; break; } @@ -281,7 +151,7 @@ DeviceEnumerator *DeviceEnumerator::create() DeviceEnumerator::~DeviceEnumerator() { - for (DeviceInfo *dev : devices_) { + for (MediaDevice *dev : devices_) { if (dev->busy()) LOG(Error) << "Removing device info while still in use"; @@ -300,171 +170,62 @@ DeviceEnumerator::~DeviceEnumerator() */ int DeviceEnumerator::addDevice(const std::string &devnode) { - int fd, ret; + MediaDevice *media = new MediaDevice(devnode); - struct media_device_info info = {}; - std::map entities; - - fd = open(devnode.c_str(), O_RDWR); - if (fd < 0) { - ret = -errno; - LOG(Info) << "Unable to open " << devnode << - " (" << strerror(-ret) << "), skipping"; + int ret = media->open(); + if (ret < 0) return ret; - } - ret = readInfo(fd, info); - if (ret) - goto out; - - ret = readTopology(fd, entities); - if (ret) - goto out; - - devices_.push_back(new DeviceInfo(devnode, info, entities)); -out: - close(fd); - - return ret; -} - -/** - * \brief Fetch the MEDIA_IOC_DEVICE_INFO from media device - * - * \param[in] fd File pointer to media device - * \param[out] info Information retrieved from MEDIA_IOC_DEVICE_INFO IOCTL - * - * Opens the media device and quires its information. - * - * \return 0 on success none zero otherwise - */ -int DeviceEnumerator::readInfo(int fd, struct media_device_info &info) -{ - int ret; - - ret = ioctl(fd, MEDIA_IOC_DEVICE_INFO, &info); + ret = media->populate(); if (ret < 0) { - ret = -errno; - LOG(Info) << "Unable to read device info " << + LOG(Info) << "Unable to populate media device " << devnode << " (" << strerror(-ret) << "), skipping"; return ret; } - return 0; -} - -/** - * \brief Fetch the topology from media device - * - * \param[in] fd File pointer to media device - * \param[out] entities Map of entity names to device node paths - * - * The media graph is retrieved using MEDIA_IOC_G_TOPOLOGY and the - * result is transformed to a map where the entity name is the key - * and the filesystem path for that entity device node is the value. - * - * \return 0 on success none zero otherwise - */ -int DeviceEnumerator::readTopology(int fd, std::map &entities) -{ - struct media_v2_topology topology; - struct media_v2_entity *ents = nullptr; - struct media_v2_interface *ifaces = nullptr; - struct media_v2_link *links = nullptr; - int ret; - - while (true) { - topology = {}; - - ret = ioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology); - if (ret < 0) - return -errno; - - __u64 version = topology.topology_version; - - ents = new media_v2_entity[topology.num_entities](); - ifaces = new media_v2_interface[topology.num_interfaces](); - links = new media_v2_link[topology.num_links](); - topology.ptr_entities = reinterpret_cast<__u64>(ents); - topology.ptr_interfaces = reinterpret_cast<__u64>(ifaces); - topology.ptr_links = reinterpret_cast<__u64>(links); - - ret = ioctl(fd, MEDIA_IOC_G_TOPOLOGY, &topology); - if (ret < 0) { - ret = -errno; - goto done; - } - - if (version == topology.topology_version) - break; - - delete[] links; - delete[] ifaces; - delete[] ents; - } - - for (unsigned int link_id = 0; link_id < topology.num_links; link_id++) { - unsigned int iface_id, ent_id; - std::string devnode; - - if ((links[link_id].flags & MEDIA_LNK_FL_LINK_TYPE) != - MEDIA_LNK_FL_INTERFACE_LINK) + /* Associate entities to device node paths. */ + for (MediaEntity *entity : media->entities()) { + if (entity->major() == 0 && entity->minor() == 0) continue; - for (iface_id = 0; iface_id < topology.num_interfaces; iface_id++) - if (links[link_id].source_id == ifaces[iface_id].id) - break; - - for (ent_id = 0; ent_id < topology.num_entities; ent_id++) - if (links[link_id].sink_id == ents[ent_id].id) - break; - - if (ent_id >= topology.num_entities || - iface_id >= topology.num_interfaces) - continue; + std::string devnode = lookupDevnode(entity->major(), entity->minor()); + if (devnode.empty()) + return -EINVAL; - devnode = lookupDevnode(ifaces[iface_id].devnode.major, - ifaces[iface_id].devnode.minor); - if (devnode == "") - break; - - entities[ents[ent_id].name] = devnode; + ret = entity->setDeviceNode(devnode); + if (ret) + return ret; } -done: - delete[] links; - delete[] ifaces; - delete[] ents; - return ret; + devices_.push_back(media); + media->close(); + + return 0; } /** * \brief Search available media devices for a pattern match * - * \param[in] dm search pattern + * \param[in] dm Search pattern * - * Search the enumerated media devices who are not already in use + * Search in the enumerated media devices that are not already in use * for a match described in \a dm. If a match is found and the caller - * intends to use it the caller is responsible to mark the DeviceInfo + * intends to use it the caller is responsible to mark the MediaDevice * object as in use and to release it when it's done with it. * - * \return pointer to the matching DeviceInfo, nullptr if no match is found + * \return pointer to the matching MediaDevice, nullptr if no match is found */ -DeviceInfo *DeviceEnumerator::search(DeviceMatch &dm) const +MediaDevice *DeviceEnumerator::search(DeviceMatch &dm) const { - DeviceInfo *info = nullptr; - - for (DeviceInfo *dev : devices_) { + for (MediaDevice *dev : devices_) { if (dev->busy()) continue; - if (dm.match(dev)) { - info = dev; - break; - } + if (dm.match(dev)) + return dev; } - return info; + return nullptr; } /** diff --git a/src/libcamera/include/device_enumerator.h b/src/libcamera/include/device_enumerator.h index 24bca0e3fc32..0d104667323b 100644 --- a/src/libcamera/include/device_enumerator.h +++ b/src/libcamera/include/device_enumerator.h @@ -15,29 +15,7 @@ namespace libcamera { -class DeviceInfo -{ -public: - DeviceInfo(const std::string &devnode, const struct media_device_info &info, - const std::map &entities); - - int acquire(); - void release(); - bool busy() const; - - const std::string &devnode() const; - const struct media_device_info &info() const; - std::vector entities() const; - - int lookup(const std::string &name, std::string &devnode) const; - -private: - bool acquired_; - - std::string devnode_; - struct media_device_info info_; - std::map entities_; -}; +class MediaDevice; class DeviceMatch { @@ -46,7 +24,7 @@ public: void add(const std::string &entity); - bool match(const DeviceInfo *info) const; + bool match(const MediaDevice *device) const; private: std::string driver_; @@ -63,16 +41,13 @@ public: virtual int init() = 0; virtual int enumerate() = 0; - DeviceInfo *search(DeviceMatch &dm) const; + MediaDevice *search(DeviceMatch &dm) const; protected: int addDevice(const std::string &devnode); private: - std::vector devices_; - - int readInfo(int fd, struct media_device_info &info); - int readTopology(int fd, std::map &entities); + std::vector devices_; virtual std::string lookupDevnode(int major, int minor) = 0; }; diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp index b1e2d32c8ba2..720d9c2031c9 100644 --- a/src/libcamera/pipeline/vimc.cpp +++ b/src/libcamera/pipeline/vimc.cpp @@ -8,6 +8,7 @@ #include #include "device_enumerator.h" +#include "media_device.h" #include "pipeline_handler.h" namespace libcamera { @@ -24,12 +25,12 @@ public: Camera *camera(unsigned int id) final; private: - DeviceInfo *info_; + MediaDevice *dev_; Camera *camera_; }; PipeHandlerVimc::PipeHandlerVimc() - : info_(nullptr), camera_(nullptr) + : dev_(nullptr), camera_(nullptr) { } @@ -38,8 +39,8 @@ PipeHandlerVimc::~PipeHandlerVimc() if (camera_) camera_->put(); - if (info_) - info_->release(); + if (dev_) + dev_->release(); } unsigned int PipeHandlerVimc::count() @@ -69,15 +70,15 @@ bool PipeHandlerVimc::match(DeviceEnumerator *enumerator) dm.add("RGB/YUV Input"); dm.add("Scaler"); - info_ = enumerator->search(dm); - if (!info_) + dev_ = enumerator->search(dm); + if (!dev_) return false; - info_->acquire(); + dev_->acquire(); /* * NOTE: A more complete Camera implementation could - * be passed the DeviceInfo(s) it controls here or + * be passed the MediaDevice(s) it controls here or * a reference to the PipelineHandler. Which method * will be chosen depends on how the Camera * object is modeled.