summaryrefslogtreecommitdiff
path: root/src/libcamera/pipeline/raspberrypi/vcsm.h
blob: bebe86a76450f2635afbc5f1c3161e68812ec259 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2019, Raspberry Pi (Trading) Limited
 *
 * vcsm.h - Helper class for vcsm allocations.
 */
#ifndef __LIBCAMERA_PIPELINE_RASPBERRYPI_VCSM_H__
#define __LIBCAMERA_PIPELINE_RASPBERRYPI_VCSM_H__

#include <iostream>
#include <mutex>

#include <fcntl.h>
#include <linux/vc_sm_cma_ioctl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>

namespace RPi {

#define VCSM_CMA_DEVICE_NAME "/dev/vcsm-cma"

class Vcsm
{
public:
	Vcsm()
	{
		vcsmHandle_ = ::open(VCSM_CMA_DEVICE_NAME, O_RDWR, 0);
		if (vcsmHandle_ == -1) {
			std::cerr << "Could not open vcsm device: "
				  << VCSM_CMA_DEVICE_NAME;
		}
	}

	~Vcsm()
	{
		/* Free all existing allocations. */
		auto it = allocMap_.begin();
		while (it != allocMap_.end())
			it = remove(it->first);

		if (vcsmHandle_)
			::close(vcsmHandle_);
	}

	void *alloc(const char *name, unsigned int size,
		    vc_sm_cma_cache_e cache = VC_SM_CMA_CACHE_NONE)
	{
		unsigned int pageSize = getpagesize();
		void *user_ptr;
		int ret;

		if (!name)
			return nullptr;

		/* Ask for page aligned allocation. */
		size = (size + pageSize - 1) & ~(pageSize - 1);

		struct vc_sm_cma_ioctl_alloc alloc;
		memset(&alloc, 0, sizeof(alloc));
		alloc.size = size;
		alloc.num = 1;
		alloc.cached = cache;
		alloc.handle = 0;
		memcpy(alloc.name, name, 32);

		ret = ::ioctl(vcsmHandle_, VC_SM_CMA_IOCTL_MEM_ALLOC, &alloc);

		if (ret < 0 || alloc.handle < 0) {
			std::cerr << "vcsm allocation failure for "
				  << name << std::endl;
			return nullptr;
		}

		/* Map the buffer into user space. */
		user_ptr = ::mmap(0, alloc.size, PROT_READ | PROT_WRITE,
				  MAP_SHARED, alloc.handle, 0);

		if (user_ptr == MAP_FAILED) {
			std::cerr << "vcsm mmap failure for " << name << std::endl;
			::close(alloc.handle);
			return nullptr;
		}

		std::lock_guard<std::mutex> lock(lock_);
		allocMap_.emplace(user_ptr, AllocInfo(alloc.handle,
						      alloc.size, alloc.vc_handle));

		return user_ptr;
	}

	void free(void *user_ptr)
	{
		std::lock_guard<std::mutex> lock(lock_);
		remove(user_ptr);
	}

	unsigned int getVCHandle(void *user_ptr)
	{
		std::lock_guard<std::mutex> lock(lock_);
		auto it = allocMap_.find(user_ptr);
		if (it != allocMap_.end())
			return it->second.vcHandle;

		return 0;
	}

private:
	struct AllocInfo {
		AllocInfo(int handle_, int size_, int vcHandle_)
			: handle(handle_), size(size_), vcHandle(vcHandle_)
		{
		}

		int handle;
		int size;
		uint32_t vcHandle;
	};

	/* Map of all allocations that have been requested. */
	using AllocMap = std::map<void *, AllocInfo>;

	AllocMap::iterator remove(void *user_ptr)
	{
		auto it = allocMap_.find(user_ptr);
		if (it != allocMap_.end()) {
			int handle = it->second.handle;
			int size = it->second.size;
			::munmap(user_ptr, size);
			::close(handle);
			/*
			 * Remove the allocation from the map. This returns
			 * an iterator to the next element.
			 */
			it = allocMap_.erase(it);
		}

		/* Returns an iterator to the next element. */
		return it;
	}

	AllocMap allocMap_;
	int vcsmHandle_;
	std::mutex lock_;
};

} /* namespace RPi */

#endif /* __LIBCAMERA_PIPELINE_RASPBERRYPI_VCSM_H__ */