summaryrefslogtreecommitdiff
path: root/src/v4l2
diff options
context:
space:
mode:
Diffstat (limited to 'src/v4l2')
-rw-r--r--src/v4l2/v4l2_camera.cpp9
-rw-r--r--src/v4l2/v4l2_camera.h1
-rw-r--r--src/v4l2/v4l2_camera_proxy.cpp107
-rw-r--r--src/v4l2/v4l2_camera_proxy.h18
4 files changed, 111 insertions, 24 deletions
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index 9a1ebc84..25573202 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -17,7 +17,8 @@ using namespace libcamera;
LOG_DECLARE_CATEGORY(V4L2Compat);
V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
- : camera_(camera), isRunning_(false), bufferAllocator_(nullptr)
+ : camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
+ efd_(-1)
{
camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
}
@@ -29,7 +30,6 @@ V4L2Camera::~V4L2Camera()
int V4L2Camera::open()
{
- /* \todo Support multiple open. */
if (camera_->acquire() < 0) {
LOG(V4L2Compat, Error) << "Failed to acquire camera";
return -EINVAL;
@@ -59,6 +59,11 @@ void V4L2Camera::bind(int efd)
efd_ = efd;
}
+void V4L2Camera::unbind()
+{
+ efd_ = -1;
+}
+
void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)
{
*streamConfig = config_->at(0);
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index 33f5eb02..30114ed5 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -40,6 +40,7 @@ public:
int open();
void close();
void bind(int efd);
+ void unbind();
void getStreamConfig(StreamConfiguration *streamConfig);
std::vector<Buffer> completedBuffers();
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 {
diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
index b2197ef8..36d1dbc8 100644
--- a/src/v4l2/v4l2_camera_proxy.h
+++ b/src/v4l2/v4l2_camera_proxy.h
@@ -33,7 +33,6 @@ public:
void *mmap(void *addr, size_t length, int prot, int flags, off64_t offset);
int munmap(void *addr, size_t length);
- void bind(int fd);
int ioctl(V4L2CameraFile *file, unsigned long request, void *arg);
private:
@@ -44,7 +43,7 @@ private:
void querycap(std::shared_ptr<Camera> camera);
void tryFormat(struct v4l2_format *arg);
void updateBuffers();
- int freeBuffers();
+ void freeBuffers();
int vidioc_querycap(struct v4l2_capability *arg);
int vidioc_enum_fmt(V4L2CameraFile *file, struct v4l2_fmtdesc *arg);
@@ -58,6 +57,10 @@ private:
int vidioc_streamon(V4L2CameraFile *file, int *arg);
int vidioc_streamoff(V4L2CameraFile *file, int *arg);
+ bool hasOwnership(V4L2CameraFile *file);
+ int acquire(V4L2CameraFile *file);
+ void release(V4L2CameraFile *file);
+
static unsigned int bplMultiplier(uint32_t format);
static unsigned int imageSize(uint32_t format, unsigned int width,
unsigned int height);
@@ -80,7 +83,16 @@ private:
std::unique_ptr<V4L2Camera> vcam_;
- int efd_;
+ /*
+ * This is the exclusive owner of this V4L2CameraProxy instance.
+ * When there is no owner, anybody can call any ioctl before reqbufs.
+ * The first file to call reqbufs with count > 0 or s_fmt will become
+ * the owner, and when the owner calls reqbufs with count = 0 it will
+ * release ownership. Any buffer-related ioctl (except querybuf) or
+ * s_fmt that is called by a non-owner while there exists an owner
+ * will return -EBUSY.
+ */
+ V4L2CameraFile *owner_;
};
#endif /* __V4L2_CAMERA_PROXY_H__ */