summaryrefslogtreecommitdiff
path: root/test/event.cpp
blob: 9f7b1ed429d34d2f78f9da1a033f3a8b822b3dca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * Event test
 */

#include <iostream>
#include <string.h>
#include <unistd.h>

#include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/event_notifier.h>
#include <libcamera/base/thread.h>
#include <libcamera/base/timer.h>

#include "test.h"

using namespace libcamera;
using namespace std;
using namespace std::chrono_literals;

class EventTest : public Test
{
protected:
	void readReady()
	{
		size_ = read(notifier_->fd(), data_, sizeof(data_));
		notified_ = true;
	}

	int init()
	{
		notifier_ = nullptr;

		return pipe(pipefd_);
	}

	int run()
	{
		EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
		std::string data("H2G2");
		Timer timeout;
		ssize_t ret;

		notifier_ = new EventNotifier(pipefd_[0], EventNotifier::Read);
		notifier_->activated.connect(this, &EventTest::readReady);

		/* Test read notification with data. */
		memset(data_, 0, sizeof(data_));
		size_ = 0;

		ret = write(pipefd_[1], data.data(), data.size());
		if (ret < 0) {
			cout << "Pipe write failed" << endl;
			return TestFail;
		}

		timeout.start(100ms);
		dispatcher->processEvents();
		timeout.stop();

		if (static_cast<size_t>(size_) != data.size()) {
			cout << "Event notifier read ready test failed" << endl;
			return TestFail;
		}

		/* Test read notification without data. */
		notified_ = false;

		timeout.start(100ms);
		dispatcher->processEvents();
		timeout.stop();

		if (notified_) {
			cout << "Event notifier read no ready test failed" << endl;
			return TestFail;
		}

		/* Test read notifier disabling. */
		notified_ = false;
		notifier_->setEnabled(false);

		ret = write(pipefd_[1], data.data(), data.size());
		if (ret < 0) {
			cout << "Pipe write failed" << endl;
			return TestFail;
		}

		timeout.start(100ms);
		dispatcher->processEvents();
		timeout.stop();

		if (notified_) {
			cout << "Event notifier read disabling failed" << endl;
			return TestFail;
		}

		/* Test read notifier enabling. */
		notified_ = false;
		notifier_->setEnabled(true);

		timeout.start(100ms);
		dispatcher->processEvents();
		timeout.stop();

		if (!notified_) {
			cout << "Event notifier read enabling test failed" << endl;
			return TestFail;
		}

		return TestPass;
	}

	void cleanup()
	{
		delete notifier_;

		close(pipefd_[0]);
		close(pipefd_[1]);
	}

private:
	int pipefd_[2];

	EventNotifier *notifier_;
	bool notified_;
	char data_[16];
	ssize_t size_;
};

TEST_REGISTER(EventTest)
locator::DmaBufAllocatorFlags * \brief A bitwise combination of DmaBufAllocator::DmaBufAllocatorFlag values */ /** * \brief Construct a DmaBufAllocator of a given type * \param[in] type The type(s) of the dma-buf providers to allocate from * * The dma-buf provider type is selected with the \a type parameter, which * defaults to the CMA heap. If no provider of the given type can be accessed, * the constructed DmaBufAllocator instance is invalid as indicated by * the isValid() function. * * Multiple types can be selected by combining type flags, in which case * the constructed DmaBufAllocator will match one of the types. If multiple * requested types can work on the system, which provider is used is undefined. */ DmaBufAllocator::DmaBufAllocator(DmaBufAllocatorFlags type) { for (const auto &info : providerInfos) { if (!(type & info.type)) continue; int ret = ::open(info.deviceNodeName, O_RDWR | O_CLOEXEC, 0); if (ret < 0) { ret = errno; LOG(DmaBufAllocator, Debug) << "Failed to open " << info.deviceNodeName << ": " << strerror(ret); continue; } LOG(DmaBufAllocator, Debug) << "Using " << info.deviceNodeName; providerHandle_ = UniqueFD(ret); type_ = info.type; break; } if (!providerHandle_.isValid()) LOG(DmaBufAllocator, Error) << "Could not open any dma-buf provider"; } /** * \brief Destroy the DmaBufAllocator instance */ DmaBufAllocator::~DmaBufAllocator() = default; /** * \fn DmaBufAllocator::isValid() * \brief Check if the DmaBufAllocator instance is valid * \return True if the DmaBufAllocator is valid, false otherwise */ /* uClibc doesn't provide the file sealing API. */ #ifndef __DOXYGEN__ #if not HAVE_FILE_SEALS #define F_ADD_SEALS 1033 #define F_SEAL_SHRINK 0x0002 #endif #endif UniqueFD DmaBufAllocator::allocFromUDmaBuf(const char *name, std::size_t size) { /* Size must be a multiple of the page size. Round it up. */ std::size_t pageMask = sysconf(_SC_PAGESIZE) - 1; size = (size + pageMask) & ~pageMask; #if HAVE_MEMFD_CREATE int ret = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); #else int ret = syscall(SYS_memfd_create, name, MFD_ALLOW_SEALING | MFD_CLOEXEC); #endif if (ret < 0) { ret = errno; LOG(DmaBufAllocator, Error) << "Failed to allocate memfd storage for " << name << ": " << strerror(ret); return {}; } UniqueFD memfd(ret); ret = ftruncate(memfd.get(), size); if (ret < 0) { ret = errno; LOG(DmaBufAllocator, Error) << "Failed to set memfd size for " << name << ": " << strerror(ret); return {}; } /* udmabuf dma-buffers *must* have the F_SEAL_SHRINK seal. */ ret = fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SHRINK); if (ret < 0) { ret = errno; LOG(DmaBufAllocator, Error) << "Failed to seal the memfd for " << name << ": " << strerror(ret); return {}; } struct udmabuf_create create; create.memfd = memfd.get(); create.flags = UDMABUF_FLAGS_CLOEXEC; create.offset = 0; create.size = size; ret = ::ioctl(providerHandle_.get(), UDMABUF_CREATE, &create); if (ret < 0) { ret = errno; LOG(DmaBufAllocator, Error) << "Failed to create dma buf for " << name << ": " << strerror(ret); return {}; } /* The underlying memfd is kept as as a reference in the kernel. */ return UniqueFD(ret); } UniqueFD DmaBufAllocator::allocFromHeap(const char *name, std::size_t size) { struct dma_heap_allocation_data alloc = {}; int ret; alloc.len = size; alloc.fd_flags = O_CLOEXEC | O_RDWR; ret = ::ioctl(providerHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); if (ret < 0) { LOG(DmaBufAllocator, Error) << "dma-heap allocation failure for " << name; return {}; } UniqueFD allocFd(alloc.fd); ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); if (ret < 0) { LOG(DmaBufAllocator, Error) << "dma-heap naming failure for " << name; return {}; } return allocFd; } /** * \brief Allocate a dma-buf from the DmaBufAllocator * \param [in] name The name to set for the allocated buffer * \param [in] size The size of the buffer to allocate * * Allocates a dma-buf with read/write access. * * If the allocation fails, return an invalid UniqueFD. * * \return The UniqueFD of the allocated buffer */ UniqueFD DmaBufAllocator::alloc(const char *name, std::size_t size) { if (!name) return {}; if (type_ == DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf) return allocFromUDmaBuf(name, size); else return allocFromHeap(name, size); } } /* namespace libcamera */