libcamera: device_enumerator: Use MediaDevice
Replace usage of the DeviceInfo class with MediaDevice in the DeviceEnumerator and remove the DeviceInfo class. Signed-off-by: Jacopo Mondi <jacopo@jmondi.org> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
d4af90d729
commit
d83269aae0
3 changed files with 54 additions and 317 deletions
|
@ -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<std::string, std::string> &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<std::string> DeviceInfo::entities() const
|
||||
{
|
||||
std::vector<std::string> 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<std::string, std::string> 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;
|
||||
}
|
||||
|
||||
/* Associate entities to device node paths. */
|
||||
for (MediaEntity *entity : media->entities()) {
|
||||
if (entity->major() == 0 && entity->minor() == 0)
|
||||
continue;
|
||||
|
||||
std::string devnode = lookupDevnode(entity->major(), entity->minor());
|
||||
if (devnode.empty())
|
||||
return -EINVAL;
|
||||
|
||||
ret = entity->setDeviceNode(devnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
devices_.push_back(media);
|
||||
media->close();
|
||||
|
||||
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<std::string, std::string> &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)
|
||||
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;
|
||||
|
||||
devnode = lookupDevnode(ifaces[iface_id].devnode.major,
|
||||
ifaces[iface_id].devnode.minor);
|
||||
if (devnode == "")
|
||||
break;
|
||||
|
||||
entities[ents[ent_id].name] = devnode;
|
||||
}
|
||||
done:
|
||||
delete[] links;
|
||||
delete[] ifaces;
|
||||
delete[] ents;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* \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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,29 +15,7 @@
|
|||
|
||||
namespace libcamera {
|
||||
|
||||
class DeviceInfo
|
||||
{
|
||||
public:
|
||||
DeviceInfo(const std::string &devnode, const struct media_device_info &info,
|
||||
const std::map<std::string, std::string> &entities);
|
||||
|
||||
int acquire();
|
||||
void release();
|
||||
bool busy() const;
|
||||
|
||||
const std::string &devnode() const;
|
||||
const struct media_device_info &info() const;
|
||||
std::vector<std::string> 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<std::string, std::string> 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<DeviceInfo *> devices_;
|
||||
|
||||
int readInfo(int fd, struct media_device_info &info);
|
||||
int readTopology(int fd, std::map<std::string, std::string> &entities);
|
||||
std::vector<MediaDevice *> devices_;
|
||||
|
||||
virtual std::string lookupDevnode(int major, int minor) = 0;
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <libcamera/camera.h>
|
||||
|
||||
#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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue