[libcamera-devel] [PATCH RFC v2] libcamera: enumeration: Generalize matching
Sophie Friedrich
dev at flowerpot.me
Mon Jun 26 19:22:04 CEST 2023
The issue at hand is, that the current implementation of how devices are
matched
to their pipeline handlers is insufficient for future requirements as it
doesn't
generalize well to pipeline handlers which would directly find USB
devices or
initialize virtual test devices using e.g. a config file.
After initial discussions in the accompanying IRC it has become clear
that the
original idea proposed by my previous patch (see [1]) regarding this
topic is
insufficient as it replaced a makeshift implementation with an only
marginally
better solution. In accordance to that this RFC should better outline the
problem and help in finding a good and future-proof solution.
## How does device matching work right now
The terminology and internal procedures has become quite confusing, so
this is a
small reminder on how it (normally) works rn on main.
1. `CameraManager::Private::init` creates a `DeviceEnumerator`
- the enumerator is responsible for discovering devices (includes
hot-plugging)
- devices are of instance `MediaDevice`
2. `::createPipelineHandlers` is called on a list of devices and pipelines
- creates a pipeline by calling
`PipelineHandler::match(DeviceEnumerator*)`
3. `PipelineHandler::match` is a virtual function, but the most common
implementation does two steps relevant to matching
1. it initializes and populates a `DeviceMatch` class using a driver
and list
of entities. The latter is done using `DeviceMatch::add(std::string*)`
2. it (indirectly) calls `DeviceEnumerator::search` using the
`DeviceMatch`
instance and the previously enumerated devices
4. `DeviceEnumerator::search` filters the list of devices by calling
`DeviceMatch::match(MediaDevice*)` for each device
The restrictions on filtering/matching are currently encoded in the
implementation of `DeviceMatch::match` and `MediaDevice`. The former
function
first string compares `DeviceMatch::driver_` against
`MediaDevice::driver()` and
afterwards string compares all `DeviceMatch::entities_` against all
`MediaDevice::entities()[:].name()`. Only if for each `DeviceMatch` entity a
corresponding `MediaDevice` entity could be found, the given device is
accepted.
## Proposed changes
My previous proposal (see [1]) was to expand `DeviceMatch` by using a
map with
pairs of (`std::string` property name, `std::string` value). In order to not
disrupt the current pipeline code a catch-all property name ('*') was
introduced, with which `DeviceMatch` could be used as before, but
allowed more
advanced filtering if required.
One major critique has been, that the specific filter criteria were still
defined by how `DeviceMatch::match()` worked. Instead of comparing
string entity
names it now compared string entity (key/value) pairs. (see comment by
J. Mondi
in [2])
In the IRC two alternative ideas were proposed by J. Mondy to solve this
issue:
1. `DeviceMatch::add()` could accept differing types of parameters with each
pipeline handler choosing what they want. Matching would still happen in
`DeviceMatch::match`
2. Have `DeviceMatch` an abstract class with derived `MediaDeviceMatch`,
`USBDeviceMatch`, … classes that implement their own `::add` and
`::match`
functions. The `DeviceEnumerator` would then create the correct
`MediaDevice`, `USBDevice` classes and pass them as `Device*` to the
virtual
`DeviceMatch::match` function.
Additionally, I would like to clear up the current terminology. I think
renaming
`DeviceMatch` to e.g. `DeviceFilter` would more sharply explain its
function as
it basically just describes filter criteria for matching. Currently, the
word
`match` seems to me a bit overused.
### My own opinion
I personally prefer idea (2) over idea (1), as it allows restricting and
naming
the possible filter criteria in code, while at the same time being abstract
enough to allow a wide range of matching. For e.g. `USBDeviceMatch` this
could
mean that the possible matchable property names are very limited on
purpose. See
the Linux kernel internal `usb_match_id` struct for example [3], which is
responsible for defining the filtering criteria for USB drivers.
As (1) in its initial idea doesn't have this property, it seems to be
lacking in
similar regards as my initial proposal.
One issue with both proposals that I see, is that C++ `dynamic_cast` (or
similar) would need to be used internally using a naive approach.
Although I'm
unsure if this even really is a problem. The performance penalty could be
irrelevant and maybe RTTI support is already assumed.
Best regards
Sophie
[1] https://patchwork.libcamera.org/patch/18328/
[2] https://patchwork.libcamera.org/patch/18329/
[3]
https://elixir.bootlin.com/linux/v6.4/source/include/linux/mod_devicetable.h#L128
---
v1 -> RFC: moved from possible implementation back to discussion stage
More information about the libcamera-devel
mailing list