[libcamera-devel] [PATCH 3/5] libcamera: Add MediaLink link setup function
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Mon Jan 7 22:50:49 CET 2019
Hello,
On Monday, 7 January 2019 10:41:17 EET Jacopo Mondi wrote:
> On Fri, Jan 04, 2019 at 05:45:09PM +0100, Niklas Söderlund wrote:
> > On 2019-01-03 18:38:57 +0100, Jacopo Mondi wrote:
> > > Add a function to the MediaLink class to set the state of a link to
> > > enabled or disabled. The function makes use of an internal MediaDevice
> > > method, which is defined private and only accessible by the MediaLink
> > > setup() function itself.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> > > ---
> > >
> > > src/libcamera/include/media_device.h | 8 ++++++
> > > src/libcamera/include/media_object.h | 1 +
> > > src/libcamera/media_device.cpp | 30 +++++++++++++++++++++
> > > src/libcamera/media_object.cpp | 40 ++++++++++++++++++++++++++++
> > > 4 files changed, 79 insertions(+)
> > >
> > > diff --git a/src/libcamera/include/media_device.h
> > > b/src/libcamera/include/media_device.h index 3228ad5..d019639 100644
> > > --- a/src/libcamera/include/media_device.h
> > > +++ b/src/libcamera/include/media_device.h
> > > @@ -65,6 +65,14 @@ private:
> > > bool populateEntities(const struct media_v2_topology &topology);
> > > bool populatePads(const struct media_v2_topology &topology);
> > > bool populateLinks(const struct media_v2_topology &topology);
> > > +
> > > + /*
> > > + * The MediaLink enable method needs to access the private
> > > + * setLink() function.
> > > + */
Do we need this comment ?
> > > + friend int MediaLink::enable(bool enable);
> >
> > I'm not saying we won't need this. But reading this makes me a bit
> > uncomfortable that the over all design is not sound. Would it not be OK
> > to make setLink() public instead? This interface will not be exposed to
> > applications so I don't feel there is a great need to protect us from us
> > self :-)
>
> I discussed it privately as well, as my initial idea was to have all
> of this link handling operations going through the media device object,
> in which case a public setLink() function would be the one the pipeline
> handlers would have to use. To me that would have made possible to
> serialize operations and guarantee that a configuration on the media
> graph is applied entirely, and through a single entity-oriented API,
> instead of providing an interface to retrieve a link and then operate
> on it asynchronously.
>
> I've been convinced that for now is better to let pipeline handlers
> to retrieve links and then enable/disable them singularly, as the
> 'MediaLink::enable()' operation does, and in that design I had to do a
> few things I'm not super happy with:
> - Add a media device reference to MediaObjects
> - Add the here below friend method so that i can call setLink (which
> in this way, is at least not public)
>
> What are your concerns on this design for thinking it is not sound?
>
> Ah, a note: we don't have to protect us from ourselves (which is not
> totally true anyway...) but remember pipeline handlers will be
> implemented by anyone who has a platform to run libcamera on, and it
> is important we define an unambigous interface to provide them.
>
> > > + int setLink(const MediaPad *source, const MediaPad *sink,
> > > + unsigned int flags);
Why do you pass the source and sink pointers instead of the link pointer ?
> > > };
> > >
> > > } /* namespace libcamera */
> > > diff --git a/src/libcamera/include/media_object.h
> > > b/src/libcamera/include/media_object.h index 0f0bb29..e00c639 100644
> > > --- a/src/libcamera/include/media_object.h
> > > +++ b/src/libcamera/include/media_object.h
> > > @@ -40,6 +40,7 @@ public:
> > > MediaPad *source() const { return source_; }
> > > MediaPad *sink() const { return sink_; }
> > > unsigned int flags() const { return flags_; }
> > >
> > > + int enable(bool enable);
Should this be called setEnabled() to follow the "getters are named as
properties, setters with a set prefix" rule ? Furthermore disabling a link by
calling the enable() method is a bit confusing :-)
> > > private:
> > > friend class MediaDevice;
> > >
> > > diff --git a/src/libcamera/media_device.cpp
> > > b/src/libcamera/media_device.cpp index 6892300..b86d0c4 100644
> > > --- a/src/libcamera/media_device.cpp
> > > +++ b/src/libcamera/media_device.cpp
> > > @@ -641,4 +641,34 @@ bool MediaDevice::populateLinks(const struct
> > > media_v2_topology &topology)
> > > return true;
> > > }
> > >
> > > +int MediaDevice::setLink(const MediaPad *source, const MediaPad *sink,
> > > + unsigned int flags)
> > > +{
> > > + struct media_link_desc linkDesc = { };
> > > +
> > > + linkDesc.source.entity = source->entity()->id();
> > > + linkDesc.source.index = source->index();
> > > + linkDesc.source.flags = MEDIA_PAD_FL_SOURCE;
> > > +
> > > + linkDesc.sink.entity = sink->entity()->id();
> > > + linkDesc.sink.index = sink->index();
> > > + linkDesc.sink.flags = MEDIA_PAD_FL_SINK;
> > > +
> > > + linkDesc.flags = flags;
> > > +
> > > + int ret = ioctl(fd_, MEDIA_IOC_SETUP_LINK, &linkDesc);
> > > + if (ret) {
> > > + ret = -errno;
> > > + LOG(Error) << "Failed to setup link: " << strerror(-ret) << "\n";
> > > + return ret;
> > > + }
> > > +
> > > + LOG(Debug) << source->entity()->name() << "["
> > > + << source->index() << "] -> "
> > > + << sink->entity()->name() << "["
> > > + << sink->index() << "]: " << flags;
> >
> > Should you not log linkDesc.flags here in case the IOCTL changed the
> > flags? Or maybe even compare flags to linkDesc.flags to make sure the
> > flags you want set is set. This is a open question and I'm fine with
> > this approach if MEDIA_IOC_SETUP_LINK can not modify the flags.
> >
> > > +
> > > + return 0;
> > > +}
> > > +
> > > } /* namespace libcamera */
> > > diff --git a/src/libcamera/media_object.cpp
> > > b/src/libcamera/media_object.cpp index f1535e6..1ee8823 100644
> > > --- a/src/libcamera/media_object.cpp
> > > +++ b/src/libcamera/media_object.cpp
> > > @@ -16,6 +16,7 @@
> > >
> > > #include "log.h"
> > > #include "media_object.h"
> > > +#include "media_device.h"
Alphabetical order please :-)
> > >
> > > /**
> > > * \file media_object.h
> > > @@ -87,6 +88,45 @@ namespace libcamera {
> > > * Each link is referenced in the link array of both of the pads it
> > > connect.
> > > */
> > >
> > > +/**
> > > + * \brief Set a link state to enabled or disabled
> >
> > Enable or disable a link in the media graph
> >
> > > + * \param enable The enable flag, true enables the link, false disables
> > > it
From the point of view of this method, it's not a link flag, but an enable/
disable boolean, so I wouldn't mention flag here.
* \param enable True to enable the link, false to disable it
> > > + *
> > > + * Set the status of a link, according to the value of \a enable.
> > > + * Links between two pads can be set to the enabled or disabled state
> > > freely, + * unless they're immutable links, whose status cannot be
> > > changed. + * Enabling an immutable link is not considered an error,
> > > while trying to + * disable it is. Caller should inspect the link flags
> > > before trying to + * disable an immutable link.
Shouldn't we reject attempts to enable immutable links ? At best they're no-op
that should be removed, and at worst they're bugs and should be fixed.
> > > + *
> > > + * Enabling a link establishes a data connection between two pads,
> > > while
> > > + * disabling it interrupts such a connections.
> > > + *
> > > + * \return 0 for success, negative error code otherwise
I think we usually write this as "0 on success, or a negative error code
otherwise".
> > > + */
> > > +int MediaLink::enable(bool enable)
> > > +{
> > > + unsigned int flags = enable ? MEDIA_LNK_FL_ENABLED : 0;
> > > +
> > > + if ((flags_ & MEDIA_LNK_FL_IMMUTABLE) && !enable) {
> > > + LOG(Error) << "Immutable links cannot be disabled";
> > > + return -EINVAL;
> > > + }
> >
> > Is it sound to relay on cached values here? What if the kernel have
> > changed the state of the link to once more be mutable (not sure any
> > drivers to this today)?
No driver does this, and the spec doesn't really allow it.
> > I would drop this check and let the kernel deal with this and relay on the
> > return code from the MEDIA_IOC_SETUP_LINK ioctl.
We indeed don't really need to optimize the error code path.
> > > +
> > > + /* Do not try to enable an already enabled link. */
> > > + if ((flags_ & MEDIA_LNK_FL_ENABLED) && enable)
> > > + return 0;
> >
> > Same here, I would not trust the cached value. What if another process
> > have modified the media graph unknown to the library? Better let the
> > request happen and let the kernel deal with it.
We're likely screwed in that case anyway, but I agree there's no real need to
optimize this.
> > > +
> > > + int ret = media_->setLink(source_, sink_, flags);
> > > + if (ret)
> > > + return ret;
> > > +
> > > + /* Only update flags if 'setLink()' didn't fail. */
I think you can drop this comment.
> > > + flags_ = flags;
> > > +
> > > + return 0;
> >
> > I think you can drop the comment and change this to:
Ah indeed :-)
> > int ret = media_->setLink(source_, sink_, flags);
> >
> > if (!ret)
> > flags_ = flags;
I would have kept the existing test though, I find the code more readable when
error paths returns as early as possible. It also makes it easier to later add
code between the setLink() call and the flags_ assignment.
> > return ret;
> > >
> > > +}
> > > +
> > > /**
> > > * \brief Construct a MediaLink
> > > * \param media The media device this entity belongs to
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list