-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
- Loading branch information
Showing
6 changed files
with
248 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* SPDX-License-Identifier: LGPL-2.1-or-later */ | ||
/* | ||
* Copyright (C) 2022, Ideas on Board Oy | ||
* | ||
* Allocate buffers for use and sharing from /dev/udmabuf | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <stddef.h> | ||
#include <vector> | ||
|
||
#include <libcamera/base/unique_fd.h> | ||
|
||
namespace libcamera { | ||
|
||
class Camera; | ||
class Stream; | ||
class FrameBuffer; | ||
|
||
class UdmaBuf | ||
{ | ||
public: | ||
UdmaBuf(); | ||
bool isValid() const { return allocator_.isValid(); } | ||
UniqueFD allocate(const char *name, std::size_t size); | ||
|
||
int exportFrameBuffers(Camera *camera, Stream *stream, | ||
std::vector<std::unique_ptr<FrameBuffer>> *buffers); | ||
|
||
private: | ||
UniqueFD allocator_; | ||
std::unique_ptr<FrameBuffer> createBuffer(std::size_t size); | ||
}; | ||
|
||
} /* namespace libcamera */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/* SPDX-License-Identifier: LGPL-2.1-or-later */ | ||
/* | ||
* Copyright (C) 2022, Ideas on Board Oy | ||
* | ||
* Allocate buffers for use and sharing from /dev/udmabuf | ||
*/ | ||
|
||
#include "libcamera/internal/udma_allocator.h" | ||
|
||
#include <fcntl.h> | ||
#include <sys/ioctl.h> | ||
#include <sys/mman.h> | ||
#include <sys/stat.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
#include <linux/udmabuf.h> | ||
|
||
#include <libcamera/base/log.h> | ||
|
||
#include <libcamera/stream.h> | ||
|
||
namespace libcamera { | ||
|
||
LOG_DEFINE_CATEGORY(UDMA) | ||
|
||
UdmaBuf::UdmaBuf() | ||
{ | ||
int ret = ::open("/dev/udmabuf", O_RDWR, 0); | ||
if (ret < 0) { | ||
ret = errno; | ||
LOG(UDMA, Error) | ||
<< "Failed to open allocator: " << strerror(ret); | ||
|
||
if (ret == EACCES) { | ||
LOG(UDMA, Info) | ||
<< "Consider making /dev/udmabuf accessible by the video group"; | ||
LOG(UDMA, Info) | ||
<< "Alternatively, add your user to the kvm group."; | ||
} | ||
} else { | ||
allocator_ = UniqueFD(ret); | ||
} | ||
} | ||
|
||
UniqueFD UdmaBuf::allocate(const char *name, std::size_t size) | ||
{ | ||
if (!isValid()) { | ||
LOG(UDMA, Fatal) << "Allocation attempted without allocator" << name; | ||
return {}; | ||
} | ||
|
||
int ret; | ||
|
||
ret = memfd_create(name, MFD_ALLOW_SEALING); | ||
if (ret < 0) { | ||
ret = errno; | ||
LOG(UDMA, Error) | ||
<< "Failed to allocate memfd storage: " | ||
<< strerror(ret); | ||
return {}; | ||
} | ||
|
||
UniqueFD memfd(ret); | ||
|
||
ret = ftruncate(memfd.get(), size); | ||
if (ret < 0) { | ||
ret = errno; | ||
LOG(UDMA, Error) | ||
<< "Failed to set memfd size: " << strerror(ret); | ||
return {}; | ||
} | ||
|
||
/* UDMA Buffers *must* have the F_SEAL_SHRINK seal */ | ||
ret = fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SHRINK); | ||
if (ret < 0) { | ||
ret = errno; | ||
LOG(UDMA, Error) | ||
<< "Failed to seal the memfd: " << strerror(ret); | ||
return {}; | ||
} | ||
|
||
struct udmabuf_create create; | ||
|
||
create.memfd = memfd.get(); | ||
create.flags = UDMABUF_FLAGS_CLOEXEC; | ||
create.offset = 0; | ||
create.size = size; | ||
|
||
ret = ::ioctl(allocator_.get(), UDMABUF_CREATE, &create); | ||
if (ret < 0) { | ||
ret = errno; | ||
LOG(UDMA, Error) | ||
<< "Failed to allocate " << size << " bytes: " | ||
<< strerror(ret); | ||
return {}; | ||
} | ||
|
||
/* The underlying memfd is kept as as a reference in the kernel */ | ||
UniqueFD uDma(ret); | ||
|
||
if (create.size != size) | ||
LOG(UDMA, Warning) | ||
<< "Allocated " << create.size << " bytes instead of " | ||
<< size << " bytes"; | ||
|
||
/* Fail if not suitable, the allocation will be free'd by UniqueFD */ | ||
if (create.size < size) | ||
return {}; | ||
|
||
LOG(UDMA, Debug) << "Allocated " << create.size << " bytes"; | ||
|
||
return uDma; | ||
} | ||
|
||
std::unique_ptr<FrameBuffer> UdmaBuf::createBuffer(std::size_t size) | ||
{ | ||
std::vector<FrameBuffer::Plane> planes; | ||
|
||
UniqueFD fd = allocate("Buffer", size); | ||
if (!fd.isValid()) | ||
return nullptr; | ||
|
||
FrameBuffer::Plane plane; | ||
plane.fd = SharedFD(std::move(fd)); | ||
plane.offset = 0; | ||
plane.length = size; | ||
|
||
planes.push_back(std::move(plane)); | ||
|
||
return std::make_unique<FrameBuffer>(planes); | ||
} | ||
|
||
int UdmaBuf::exportFrameBuffers([[maybe_unused]]Camera *camera, Stream *stream, | ||
std::vector<std::unique_ptr<FrameBuffer>> *buffers) | ||
{ | ||
unsigned int count = stream->configuration().bufferCount; | ||
|
||
/** \todo: Support multiplanar allocations */ | ||
size_t size = stream->configuration().frameSize; | ||
|
||
for (unsigned i = 0; i < count; ++i) { | ||
std::unique_ptr<FrameBuffer> buffer = createBuffer(size); | ||
if (!buffer) { | ||
LOG(UDMA, Error) << "Unable to create buffer"; | ||
|
||
buffers->clear(); | ||
return -EINVAL; | ||
} | ||
|
||
buffers->push_back(std::move(buffer)); | ||
} | ||
|
||
return count; | ||
} | ||
|
||
} /* namespace libcamera */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
/* | ||
* Copyright (C) 2022, Ideas on Board Oy. | ||
* | ||
* udma-allocator.cpp - UdmaBuf Allocator Test | ||
*/ | ||
|
||
#include <fcntl.h> | ||
#include <iostream> | ||
#include <sys/stat.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
#include "libcamera/internal/udma_allocator.h" | ||
|
||
#include "test.h" | ||
|
||
using namespace libcamera; | ||
using namespace std; | ||
|
||
class UdmaBufTest : public Test | ||
{ | ||
protected: | ||
int init() override | ||
{ | ||
allocator_ = new UdmaBuf(); | ||
|
||
return !allocator_->isValid(); | ||
} | ||
|
||
int run() override | ||
{ | ||
UniqueFD buf = allocator_->allocate("Test", 4096 * 10); | ||
|
||
std::cout << "Allocation " << (buf.isValid() ? "valid" : "failed") << std::endl; | ||
|
||
if (!buf.isValid()) | ||
return TestFail; | ||
|
||
return TestPass; | ||
} | ||
|
||
void cleanup() override | ||
{ | ||
delete allocator_; | ||
} | ||
|
||
private: | ||
UdmaBuf *allocator_; | ||
}; | ||
|
||
TEST_REGISTER(UdmaBufTest) |