[libcamera-devel,v4,1/3] libcamera: v4l2_videodevice: Add a dequeue timer
diff mbox series

Message ID 20220406075859.3857121-2-naush@raspberrypi.com
State Accepted
Headers show
Series
  • Dequeue timeout
Related show

Commit Message

Naushir Patuck April 6, 2022, 7:58 a.m. UTC
From: Naushir Patuck via libcamera-devel <libcamera-devel@lists.libcamera.org>

Add a timer that gets reset on every buffer dequeue event. If the timeout
expires, optionally call a slot in the pipeline handler to handle this
condition. This may be useful in detecting and handling stalls in either the
hardware or device driver.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/internal/v4l2_videodevice.h | 10 ++++
 src/libcamera/v4l2_videodevice.cpp            | 55 +++++++++++++++++++
 2 files changed, 65 insertions(+)

Comments

Laurent Pinchart April 6, 2022, 11:15 a.m. UTC | #1
Hi Naush,

Thank you for the patch.

On Wed, Apr 06, 2022 at 08:58:57AM +0100, Naushir Patuck wrote:
> From: Naushir Patuck via libcamera-devel <libcamera-devel@lists.libcamera.org>

This looks wrong. I'll fix it locally. Did you apply the patch to your
tree back from the mailing list ?

> Add a timer that gets reset on every buffer dequeue event. If the timeout
> expires, optionally call a slot in the pipeline handler to handle this
> condition. This may be useful in detecting and handling stalls in either the
> hardware or device driver.
> 
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  include/libcamera/internal/v4l2_videodevice.h | 10 ++++
>  src/libcamera/v4l2_videodevice.cpp            | 55 +++++++++++++++++++
>  2 files changed, 65 insertions(+)
> 
> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
> index cfeae7bd6c52..9c9493cc16ed 100644
> --- a/include/libcamera/internal/v4l2_videodevice.h
> +++ b/include/libcamera/internal/v4l2_videodevice.h
> @@ -20,7 +20,9 @@
>  #include <libcamera/base/class.h>
>  #include <libcamera/base/log.h>
>  #include <libcamera/base/signal.h>
> +#include <libcamera/base/timer.h>
>  #include <libcamera/base/unique_fd.h>
> +#include <libcamera/base/utils.h>
>  
>  #include <libcamera/color_space.h>
>  #include <libcamera/framebuffer.h>
> @@ -217,6 +219,9 @@ public:
>  	int streamOn();
>  	int streamOff();
>  
> +	void setDequeueTimeout(utils::Duration timeout);
> +	Signal<> dequeueTimeout;
> +
>  	static std::unique_ptr<V4L2VideoDevice>
>  	fromEntityName(const MediaDevice *media, const std::string &entity);
>  
> @@ -253,6 +258,8 @@ private:
>  	void bufferAvailable();
>  	FrameBuffer *dequeueBuffer();
>  
> +	void watchdogExpired();
> +
>  	V4L2Capability caps_;
>  	V4L2DeviceFormat format_;
>  	const PixelFormatInfo *formatInfo_;
> @@ -266,6 +273,9 @@ private:
>  	EventNotifier *fdBufferNotifier_;
>  
>  	State state_;
> +
> +	Timer watchdog_;
> +	utils::Duration watchdogDuration_;
>  };
>  
>  class V4L2M2MDevice
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 0830be80c553..dc5020df5c5c 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -539,6 +539,7 @@ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)
>  V4L2VideoDevice::V4L2VideoDevice(const MediaEntity *entity)
>  	: V4L2VideoDevice(entity->deviceNode())
>  {
> +	watchdog_.timeout.connect(this, &V4L2VideoDevice::watchdogExpired);
>  }
>  
>  V4L2VideoDevice::~V4L2VideoDevice()
> @@ -1726,6 +1727,9 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()
>  		return nullptr;
>  	}
>  
> +	if (watchdogDuration_.count())
> +		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_));
> +
>  	cache_->put(buf.index);
>  
>  	FrameBuffer *buffer = it->second;
> @@ -1828,6 +1832,8 @@ int V4L2VideoDevice::streamOn()
>  	}
>  
>  	state_ = State::Streaming;
> +	if (watchdogDuration_.count())
> +		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_));
>  
>  	return 0;
>  }
> @@ -1852,6 +1858,9 @@ int V4L2VideoDevice::streamOff()
>  	if (state_ != State::Streaming && queuedBuffers_.empty())
>  		return 0;
>  
> +	if (watchdogDuration_.count())
> +		watchdog_.stop();
> +
>  	ret = ioctl(VIDIOC_STREAMOFF, &bufferType_);
>  	if (ret < 0) {
>  		LOG(V4L2, Error)
> @@ -1879,6 +1888,52 @@ int V4L2VideoDevice::streamOff()
>  	return 0;
>  }
>  
> +/**
> + * \brief Set the dequeue timeout value
> + * \param[in] timeout The timeout value to be used
> + *
> + * Sets a timeout value, given by \a timeout, that will be used by a watchdog timer

I'll reflow this as the line got longer with the parameter rename.

> + * to ensure buffer dequeue events are periodically occurring when the device is
> + * streaming. The watchdog timer is only active when the device is streaming, so
> + * it is not necessary to disable it when the device stops streaming. The timeout
> + * value can be safely updated at any time.
> + *
> + * If the timer expires, the \ref V4L2VideoDevice::dequeueTimeout signal is
> + * emitted. This can typically be used by pipeline handlers to be notified of
> + * stalled devices.
> + *
> + * Set \a timeout to 0 to disable the watchdog timer.
> + */
> +void V4L2VideoDevice::setDequeueTimeout(utils::Duration timeout)
> +{
> +	watchdogDuration_ = timeout;
> +
> +	watchdog_.stop();
> +	if (watchdogDuration_.count() && state_ == State::Streaming) {
> +		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout));
> +	}

No need for curly braces, I'll drop the locally.

> +}
> +
> +/**
> + * \var V4L2VideoDevice::dequeueTimeout
> + * \brief A Signal emitted when the dequeue watchdog timer expires
> + */
> +
> +/**
> + * \brief Slot to handle an expired dequeue timer
> + *
> + * When this slot is called, the time between successive dequeue events is over
> + * the required timeout. Emit the \ref V4L2VideoDevice::dequeueTimeout signal.
> + */
> +void V4L2VideoDevice::watchdogExpired()
> +{
> +	LOG(V4L2, Warning)
> +		<< "Dequeue timer of " << watchdogDuration_
> +		<< " has expired!";

And this now holds on a single line, I'll handle it locally too.

> +
> +	dequeueTimeout.emit();
> +}
> +
>  /**
>   * \brief Create a new video device instance from \a entity in media device
>   * \a media

Patch
diff mbox series

diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
index cfeae7bd6c52..9c9493cc16ed 100644
--- a/include/libcamera/internal/v4l2_videodevice.h
+++ b/include/libcamera/internal/v4l2_videodevice.h
@@ -20,7 +20,9 @@ 
 #include <libcamera/base/class.h>
 #include <libcamera/base/log.h>
 #include <libcamera/base/signal.h>
+#include <libcamera/base/timer.h>
 #include <libcamera/base/unique_fd.h>
+#include <libcamera/base/utils.h>
 
 #include <libcamera/color_space.h>
 #include <libcamera/framebuffer.h>
@@ -217,6 +219,9 @@  public:
 	int streamOn();
 	int streamOff();
 
+	void setDequeueTimeout(utils::Duration timeout);
+	Signal<> dequeueTimeout;
+
 	static std::unique_ptr<V4L2VideoDevice>
 	fromEntityName(const MediaDevice *media, const std::string &entity);
 
@@ -253,6 +258,8 @@  private:
 	void bufferAvailable();
 	FrameBuffer *dequeueBuffer();
 
+	void watchdogExpired();
+
 	V4L2Capability caps_;
 	V4L2DeviceFormat format_;
 	const PixelFormatInfo *formatInfo_;
@@ -266,6 +273,9 @@  private:
 	EventNotifier *fdBufferNotifier_;
 
 	State state_;
+
+	Timer watchdog_;
+	utils::Duration watchdogDuration_;
 };
 
 class V4L2M2MDevice
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 0830be80c553..dc5020df5c5c 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -539,6 +539,7 @@  V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)
 V4L2VideoDevice::V4L2VideoDevice(const MediaEntity *entity)
 	: V4L2VideoDevice(entity->deviceNode())
 {
+	watchdog_.timeout.connect(this, &V4L2VideoDevice::watchdogExpired);
 }
 
 V4L2VideoDevice::~V4L2VideoDevice()
@@ -1726,6 +1727,9 @@  FrameBuffer *V4L2VideoDevice::dequeueBuffer()
 		return nullptr;
 	}
 
+	if (watchdogDuration_.count())
+		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_));
+
 	cache_->put(buf.index);
 
 	FrameBuffer *buffer = it->second;
@@ -1828,6 +1832,8 @@  int V4L2VideoDevice::streamOn()
 	}
 
 	state_ = State::Streaming;
+	if (watchdogDuration_.count())
+		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_));
 
 	return 0;
 }
@@ -1852,6 +1858,9 @@  int V4L2VideoDevice::streamOff()
 	if (state_ != State::Streaming && queuedBuffers_.empty())
 		return 0;
 
+	if (watchdogDuration_.count())
+		watchdog_.stop();
+
 	ret = ioctl(VIDIOC_STREAMOFF, &bufferType_);
 	if (ret < 0) {
 		LOG(V4L2, Error)
@@ -1879,6 +1888,52 @@  int V4L2VideoDevice::streamOff()
 	return 0;
 }
 
+/**
+ * \brief Set the dequeue timeout value
+ * \param[in] timeout The timeout value to be used
+ *
+ * Sets a timeout value, given by \a timeout, that will be used by a watchdog timer
+ * to ensure buffer dequeue events are periodically occurring when the device is
+ * streaming. The watchdog timer is only active when the device is streaming, so
+ * it is not necessary to disable it when the device stops streaming. The timeout
+ * value can be safely updated at any time.
+ *
+ * If the timer expires, the \ref V4L2VideoDevice::dequeueTimeout signal is
+ * emitted. This can typically be used by pipeline handlers to be notified of
+ * stalled devices.
+ *
+ * Set \a timeout to 0 to disable the watchdog timer.
+ */
+void V4L2VideoDevice::setDequeueTimeout(utils::Duration timeout)
+{
+	watchdogDuration_ = timeout;
+
+	watchdog_.stop();
+	if (watchdogDuration_.count() && state_ == State::Streaming) {
+		watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout));
+	}
+}
+
+/**
+ * \var V4L2VideoDevice::dequeueTimeout
+ * \brief A Signal emitted when the dequeue watchdog timer expires
+ */
+
+/**
+ * \brief Slot to handle an expired dequeue timer
+ *
+ * When this slot is called, the time between successive dequeue events is over
+ * the required timeout. Emit the \ref V4L2VideoDevice::dequeueTimeout signal.
+ */
+void V4L2VideoDevice::watchdogExpired()
+{
+	LOG(V4L2, Warning)
+		<< "Dequeue timer of " << watchdogDuration_
+		<< " has expired!";
+
+	dequeueTimeout.emit();
+}
+
 /**
  * \brief Create a new video device instance from \a entity in media device
  * \a media