summaryrefslogtreecommitdiff
path: root/src/v4l2/v4l2_camera_proxy.cpp
diff options
context:
space:
mode:
authorPaul Elder <paul.elder@ideasonboard.com>2020-06-22 18:51:20 +0900
committerPaul Elder <paul.elder@ideasonboard.com>2020-06-25 23:41:29 +0900
commit9076e88fc3ca3d3ffbd1b0d80542db96708614e7 (patch)
tree9c57889d22b6b868519b3cd5644a1df0001dcb8e /src/v4l2/v4l2_camera_proxy.cpp
parentedacd07c5e9a5080628e7cc4d057674f7f742ead (diff)
v4l2: v4l2_compat: Support multiple open
Previously, since we acquired the libcamera camera upon open(), it was impossible to support multiple open, as any subsequent opens would return error because the camera would already be acquired. To fix this, we first initialize the camera in the first call to V4L2CameraProxy::open(), just to heat up the stream format cache. We then add ownership by a V4L2CameraFile of a V4L2Camera via the V4L2CameraProxy. All vidioc ioctls prior to reqbufs > 0 (except for s_fmt) are able to access the camera without ownership. A call to reqbufs > 0 (and s_fmt) will take ownership, and the ownership will be released at reqbufs = 0. While ownership is assigned, the eventfd that should be signaled (and cleared) by V4L2Camera and V4L2CameraProxy is set to the V4L2CameraFile that has ownership, and is cleared when the ownership is released. In case close() is called without a reqbufs = 0 first, the ownership is also released on close(). We also use the V4L2CameraFile to contain all the information specific to an open instance of the file. This removes the need to keep track of such information within V4L2CameraProxy via multiple maps from int fd to info. Since V4L2 does not expect reqbufs 0 to ever return error, make V4L2CameraProxy::freeBuffers() return void. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Diffstat (limited to 'src/v4l2/v4l2_camera_proxy.cpp')
-rw-r--r--src/v4l2/v4l2_camera_proxy.cpp107
1 files changed, 88 insertions, 19 deletions
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index 301f1c08..3588734a 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -35,7 +35,7 @@ LOG_DECLARE_CATEGORY(V4L2Compat);
V4L2CameraProxy::V4L2CameraProxy(unsigned int index,
std::shared_ptr<Camera> camera)
: refcount_(0), index_(index), bufferCount_(0), currentBuf_(0),
- vcam_(std::make_unique<V4L2Camera>(camera))
+ vcam_(std::make_unique<V4L2Camera>(camera)), owner_(nullptr)
{
querycap(camera);
}
@@ -44,18 +44,29 @@ int V4L2CameraProxy::open(V4L2CameraFile *file)
{
LOG(V4L2Compat, Debug) << "Servicing open fd = " << file->efd();
+ if (refcount_++)
+ return 0;
+
+ /*
+ * We open the camera here, once, and keep it open until the last
+ * V4L2CameraFile is closed. The proxy is initially not owned by any
+ * file. The first file that calls reqbufs with count > 0 or s_fmt
+ * will become the owner, and no other file will be allowed to call
+ * buffer-related ioctls (except querybuf), set the format, or start or
+ * stop the stream until ownership is released with a call to reqbufs
+ * with count = 0.
+ */
+
int ret = vcam_->open();
if (ret < 0) {
- errno = -ret;
- return -1;
+ refcount_--;
+ return ret;
}
vcam_->getStreamConfig(&streamConfig_);
setFmtFromConfig(streamConfig_);
sizeimage_ = calculateSizeImage(streamConfig_);
- refcount_++;
-
return 0;
}
@@ -63,6 +74,7 @@ void V4L2CameraProxy::close(V4L2CameraFile *file)
{
LOG(V4L2Compat, Debug) << "Servicing close fd = " << file->efd();
+ release(file);
if (--refcount_ > 0)
return;
@@ -277,12 +289,16 @@ int V4L2CameraProxy::vidioc_s_fmt(V4L2CameraFile *file, struct v4l2_format *arg)
if (!validateBufferType(arg->type))
return -EINVAL;
+ int ret = acquire(file);
+ if (ret < 0)
+ return ret;
+
tryFormat(arg);
Size size(arg->fmt.pix.width, arg->fmt.pix.height);
- int ret = vcam_->configure(&streamConfig_, size,
- v4l2ToDrm(arg->fmt.pix.pixelformat),
- bufferCount_);
+ ret = vcam_->configure(&streamConfig_, size,
+ v4l2ToDrm(arg->fmt.pix.pixelformat),
+ bufferCount_);
if (ret < 0)
return -EINVAL;
@@ -309,19 +325,16 @@ int V4L2CameraProxy::vidioc_try_fmt(V4L2CameraFile *file, struct v4l2_format *ar
return 0;
}
-int V4L2CameraProxy::freeBuffers()
+void V4L2CameraProxy::freeBuffers()
{
LOG(V4L2Compat, Debug) << "Freeing libcamera bufs";
int ret = vcam_->streamOff();
- if (ret < 0) {
+ if (ret < 0)
LOG(V4L2Compat, Error) << "Failed to stop stream";
- return ret;
- }
+
vcam_->freeBuffers();
bufferCount_ = 0;
-
- return 0;
}
int V4L2CameraProxy::vidioc_reqbufs(V4L2CameraFile *file, struct v4l2_requestbuffers *arg)
@@ -334,10 +347,17 @@ int V4L2CameraProxy::vidioc_reqbufs(V4L2CameraFile *file, struct v4l2_requestbuf
LOG(V4L2Compat, Debug) << arg->count << " buffers requested ";
+ if (!hasOwnership(file) && owner_)
+ return -EBUSY;
+
arg->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;
- if (arg->count == 0)
- return freeBuffers();
+ if (arg->count == 0) {
+ freeBuffers();
+ release(file);
+
+ return 0;
+ }
Size size(curV4L2Format_.fmt.pix.width, curV4L2Format_.fmt.pix.height);
int ret = vcam_->configure(&streamConfig_, size,
@@ -386,6 +406,8 @@ int V4L2CameraProxy::vidioc_reqbufs(V4L2CameraFile *file, struct v4l2_requestbuf
LOG(V4L2Compat, Debug) << "Allocated " << arg->count << " buffers";
+ acquire(file);
+
return 0;
}
@@ -409,6 +431,9 @@ int V4L2CameraProxy::vidioc_qbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
<< arg->index << " fd = " << file->efd();
+ if (!hasOwnership(file))
+ return -EBUSY;
+
if (!validateBufferType(arg->type) ||
!validateMemoryType(arg->memory) ||
arg->index >= bufferCount_)
@@ -428,6 +453,9 @@ int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
{
LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << file->efd();
+ if (!hasOwnership(file))
+ return -EBUSY;
+
if (!validateBufferType(arg->type) ||
!validateMemoryType(arg->memory))
return -EINVAL;
@@ -462,6 +490,9 @@ int V4L2CameraProxy::vidioc_streamon(V4L2CameraFile *file, int *arg)
if (!validateBufferType(*arg))
return -EINVAL;
+ if (!hasOwnership(file))
+ return -EBUSY;
+
currentBuf_ = 0;
return vcam_->streamOn();
@@ -474,6 +505,9 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
if (!validateBufferType(*arg))
return -EINVAL;
+ if (!hasOwnership(file) && owner_)
+ return -EBUSY;
+
int ret = vcam_->streamOff();
for (struct v4l2_buffer &buf : buffers_)
@@ -532,10 +566,45 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
return ret;
}
-void V4L2CameraProxy::bind(int fd)
+bool V4L2CameraProxy::hasOwnership(V4L2CameraFile *file)
+{
+ return owner_ == file;
+}
+
+/**
+ * \brief Acquire exclusive ownership of the V4L2Camera
+ *
+ * \return Zero on success or if already acquired, and negative error on
+ * failure.
+ *
+ * This is sufficient for poll()ing for buffers. Events, however, are signaled
+ * on the file level, so all fds must be signaled. poll()ing from a different
+ * fd than the one that locks the device is a corner case, and is currently not
+ * supported.
+ */
+int V4L2CameraProxy::acquire(V4L2CameraFile *file)
{
- efd_ = fd;
- vcam_->bind(fd);
+ if (owner_ == file)
+ return 0;
+
+ if (owner_)
+ return -EBUSY;
+
+ vcam_->bind(file->efd());
+
+ owner_ = file;
+
+ return 0;
+}
+
+void V4L2CameraProxy::release(V4L2CameraFile *file)
+{
+ if (owner_ != file)
+ return;
+
+ vcam_->unbind();
+
+ owner_ = nullptr;
}
struct PixelFormatPlaneInfo {