diff options
-rw-r--r-- | src/v4l2/v4l2_camera.cpp | 9 | ||||
-rw-r--r-- | src/v4l2/v4l2_camera.h | 1 | ||||
-rw-r--r-- | src/v4l2/v4l2_camera_proxy.cpp | 107 | ||||
-rw-r--r-- | src/v4l2/v4l2_camera_proxy.h | 18 |
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__ */ |