Skip to content

Commit

Permalink
WIP: UDMABuf
Browse files Browse the repository at this point in the history
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
  • Loading branch information
kbingham committed Nov 23, 2022
1 parent 962c4d0 commit c028d61
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/libcamera/internal/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ libcamera_internal_headers = files([
'request.h',
'source_paths.h',
'sysfs.h',
'udma_allocator.h',
'v4l2_device.h',
'v4l2_pixelformat.h',
'v4l2_subdevice.h',
Expand Down
36 changes: 36 additions & 0 deletions include/libcamera/internal/udma_allocator.h
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 */
1 change: 1 addition & 0 deletions src/libcamera/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ libcamera_sources = files([
'stream.cpp',
'sysfs.cpp',
'transform.cpp',
'udma_allocator.cpp',
'v4l2_device.cpp',
'v4l2_pixelformat.cpp',
'v4l2_subdevice.cpp',
Expand Down
157 changes: 157 additions & 0 deletions src/libcamera/udma_allocator.cpp
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 */
1 change: 1 addition & 0 deletions test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ internal_tests = [
{'name': 'threads', 'sources': 'threads.cpp', 'dependencies': [libthreads]},
{'name': 'timer', 'sources': ['timer.cpp']},
{'name': 'timer-thread', 'sources': ['timer-thread.cpp']},
{'name': 'udma-allocator', 'sources': ['udma-allocator.cpp']},
{'name': 'unique-fd', 'sources': ['unique-fd.cpp']},
{'name': 'utils', 'sources': ['utils.cpp']},
{'name': 'yaml-parser', 'sources': ['yaml-parser.cpp']},
Expand Down
52 changes: 52 additions & 0 deletions test/udma-allocator.cpp
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)

0 comments on commit c028d61

Please sign in to comment.