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:
Jacopo Mondi 2018-12-26 11:15:55 +01:00 committed by Laurent Pinchart
parent d4af90d729
commit d83269aae0
3 changed files with 54 additions and 317 deletions

View file

@ -13,6 +13,7 @@
#include "device_enumerator.h" #include "device_enumerator.h"
#include "log.h" #include "log.h"
#include "media_device.h"
/** /**
* \file device_enumerator.h * \file device_enumerator.h
@ -28,8 +29,8 @@
* for other parts of libcamera. * for other parts of libcamera.
* *
* The DeviceEnumerator can enumerate all or specific media devices in * The DeviceEnumerator can enumerate all or specific media devices in
* the system. When a new media device is added the enumerator gathers * the system. When a new media device is added the enumerator creates a
* information about it and stores it in a DeviceInfo object. * corresponding MediaDevice instance.
* *
* The last functionality provided is the ability to search among the * The last functionality provided is the ability to search among the
* enumerate media devices for one matching information known to the * enumerate media devices for one matching information known to the
@ -42,135 +43,6 @@
namespace libcamera { 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 * \class DeviceMatch
* \brief Description of a media device search pattern * \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 * \brief Compare a search pattern with a media device
* * \param[in] device The media device
* \param[in] info Information about a enumerated media device
* *
* Matching is performed on the Linux device driver name and entity names * Matching is performed on the Linux device driver name and entity names
* from the media graph. * from the media graph.
* *
* \retval true The device described in \a info matches search pattern * \return true if the media device matches the search pattern, false otherwise
* \retval false The device described in \a info do not match search pattern
*/ */
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; return false;
for (const std::string &name : entities_) { for (const std::string &name : entities_) {
bool found = false; bool found = false;
for (const std::string &entity : info->entities()) { for (const MediaEntity *entity : device->entities()) {
if (name == entity) { if (name == entity->name()) {
found = true; found = true;
break; break;
} }
@ -281,7 +151,7 @@ DeviceEnumerator *DeviceEnumerator::create()
DeviceEnumerator::~DeviceEnumerator() DeviceEnumerator::~DeviceEnumerator()
{ {
for (DeviceInfo *dev : devices_) { for (MediaDevice *dev : devices_) {
if (dev->busy()) if (dev->busy())
LOG(Error) << "Removing device info while still in use"; LOG(Error) << "Removing device info while still in use";
@ -300,171 +170,62 @@ DeviceEnumerator::~DeviceEnumerator()
*/ */
int DeviceEnumerator::addDevice(const std::string &devnode) int DeviceEnumerator::addDevice(const std::string &devnode)
{ {
int fd, ret; MediaDevice *media = new MediaDevice(devnode);
struct media_device_info info = {}; int ret = media->open();
std::map<std::string, std::string> entities; if (ret < 0)
fd = open(devnode.c_str(), O_RDWR);
if (fd < 0) {
ret = -errno;
LOG(Info) << "Unable to open " << devnode <<
" (" << strerror(-ret) << "), skipping";
return ret; return ret;
}
ret = readInfo(fd, info); ret = media->populate();
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);
if (ret < 0) { if (ret < 0) {
ret = -errno; LOG(Info) << "Unable to populate media device " << devnode <<
LOG(Info) << "Unable to read device info " <<
" (" << strerror(-ret) << "), skipping"; " (" << strerror(-ret) << "), skipping";
return ret; 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; 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 * \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 * 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. * 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 (MediaDevice *dev : devices_) {
for (DeviceInfo *dev : devices_) {
if (dev->busy()) if (dev->busy())
continue; continue;
if (dm.match(dev)) { if (dm.match(dev))
info = dev; return dev;
break;
}
} }
return info; return nullptr;
} }
/** /**

View file

@ -15,29 +15,7 @@
namespace libcamera { namespace libcamera {
class DeviceInfo class MediaDevice;
{
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 DeviceMatch class DeviceMatch
{ {
@ -46,7 +24,7 @@ public:
void add(const std::string &entity); void add(const std::string &entity);
bool match(const DeviceInfo *info) const; bool match(const MediaDevice *device) const;
private: private:
std::string driver_; std::string driver_;
@ -63,16 +41,13 @@ public:
virtual int init() = 0; virtual int init() = 0;
virtual int enumerate() = 0; virtual int enumerate() = 0;
DeviceInfo *search(DeviceMatch &dm) const; MediaDevice *search(DeviceMatch &dm) const;
protected: protected:
int addDevice(const std::string &devnode); int addDevice(const std::string &devnode);
private: private:
std::vector<DeviceInfo *> devices_; std::vector<MediaDevice *> devices_;
int readInfo(int fd, struct media_device_info &info);
int readTopology(int fd, std::map<std::string, std::string> &entities);
virtual std::string lookupDevnode(int major, int minor) = 0; virtual std::string lookupDevnode(int major, int minor) = 0;
}; };

View file

@ -8,6 +8,7 @@
#include <libcamera/camera.h> #include <libcamera/camera.h>
#include "device_enumerator.h" #include "device_enumerator.h"
#include "media_device.h"
#include "pipeline_handler.h" #include "pipeline_handler.h"
namespace libcamera { namespace libcamera {
@ -24,12 +25,12 @@ public:
Camera *camera(unsigned int id) final; Camera *camera(unsigned int id) final;
private: private:
DeviceInfo *info_; MediaDevice *dev_;
Camera *camera_; Camera *camera_;
}; };
PipeHandlerVimc::PipeHandlerVimc() PipeHandlerVimc::PipeHandlerVimc()
: info_(nullptr), camera_(nullptr) : dev_(nullptr), camera_(nullptr)
{ {
} }
@ -38,8 +39,8 @@ PipeHandlerVimc::~PipeHandlerVimc()
if (camera_) if (camera_)
camera_->put(); camera_->put();
if (info_) if (dev_)
info_->release(); dev_->release();
} }
unsigned int PipeHandlerVimc::count() unsigned int PipeHandlerVimc::count()
@ -69,15 +70,15 @@ bool PipeHandlerVimc::match(DeviceEnumerator *enumerator)
dm.add("RGB/YUV Input"); dm.add("RGB/YUV Input");
dm.add("Scaler"); dm.add("Scaler");
info_ = enumerator->search(dm); dev_ = enumerator->search(dm);
if (!info_) if (!dev_)
return false; return false;
info_->acquire(); dev_->acquire();
/* /*
* NOTE: A more complete Camera implementation could * 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 * a reference to the PipelineHandler. Which method
* will be chosen depends on how the Camera * will be chosen depends on how the Camera
* object is modeled. * object is modeled.