1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include "interface/mmal/mmal_logging.h"
28#include "interface/mmal/mmal.h"
29#include "interface/vcos/vcos.h"
30
31#include "interface/mmal/vc/mmal_vc_shm.h"
32
33#ifdef ENABLE_MMAL_VCSM
34# include "user-vcsm.h"
35#endif /* ENABLE_MMAL_VCSM */
36
37#define MMAL_VC_PAYLOAD_ELEM_MAX 512
38
39typedef struct MMAL_VC_PAYLOAD_ELEM_T
40{
41 struct MMAL_VC_PAYLOAD_ELEM_T *next;
42 void *handle;
43 void *vc_handle;
44 uint8_t *mem;
45 MMAL_BOOL_T in_use;
46} MMAL_VC_PAYLOAD_ELEM_T;
47
48typedef struct MMAL_VC_PAYLOAD_LIST_T
49{
50 MMAL_VC_PAYLOAD_ELEM_T list[MMAL_VC_PAYLOAD_ELEM_MAX];
51 VCOS_MUTEX_T lock;
52} MMAL_VC_PAYLOAD_LIST_T;
53
54static int mmal_vc_shm_initialised;
55static MMAL_VC_PAYLOAD_LIST_T mmal_vc_payload_list;
56static VCOS_ONCE_T once = VCOS_ONCE_INIT;
57static VCOS_MUTEX_T refcount_lock;
58
59static void mmal_vc_shm_init_once(void)
60{
61 vcos_mutex_create(&refcount_lock, VCOS_FUNCTION);
62}
63
64static void mmal_vc_payload_list_init()
65{
66 vcos_mutex_create(&mmal_vc_payload_list.lock, "mmal_vc_payload_list");
67}
68
69static void mmal_vc_payload_list_exit()
70{
71 vcos_mutex_delete(&mmal_vc_payload_list.lock);
72}
73
74static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_get()
75{
76 MMAL_VC_PAYLOAD_ELEM_T *elem = 0;
77 unsigned int i;
78
79 vcos_mutex_lock(&mmal_vc_payload_list.lock);
80 for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++)
81 {
82 if (mmal_vc_payload_list.list[i].in_use)
83 continue;
84 elem = &mmal_vc_payload_list.list[i];
85 elem->in_use = 1;
86 break;
87 }
88 vcos_mutex_unlock(&mmal_vc_payload_list.lock);
89
90 return elem;
91}
92
93static void mmal_vc_payload_list_release(MMAL_VC_PAYLOAD_ELEM_T *elem)
94{
95 vcos_mutex_lock(&mmal_vc_payload_list.lock);
96 elem->handle = elem->vc_handle = 0;
97 elem->mem = 0;
98 elem->in_use = 0;
99 vcos_mutex_unlock(&mmal_vc_payload_list.lock);
100}
101
102static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_find_mem(uint8_t *mem)
103{
104 MMAL_VC_PAYLOAD_ELEM_T *elem = 0;
105 unsigned int i;
106
107 vcos_mutex_lock(&mmal_vc_payload_list.lock);
108 for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++)
109 {
110 if (!mmal_vc_payload_list.list[i].in_use)
111 continue;
112 if (mmal_vc_payload_list.list[i].mem != mem)
113 continue;
114 elem = &mmal_vc_payload_list.list[i];
115 break;
116 }
117 vcos_mutex_unlock(&mmal_vc_payload_list.lock);
118
119 return elem;
120}
121
122static MMAL_VC_PAYLOAD_ELEM_T *mmal_vc_payload_list_find_handle(uint8_t *mem)
123{
124 MMAL_VC_PAYLOAD_ELEM_T *elem = 0;
125 unsigned int i;
126
127 vcos_mutex_lock(&mmal_vc_payload_list.lock);
128 for (i = 0; i < MMAL_VC_PAYLOAD_ELEM_MAX; i++)
129 {
130 if (!mmal_vc_payload_list.list[i].in_use)
131 continue;
132 if (mmal_vc_payload_list.list[i].vc_handle != (void *)mem)
133 continue;
134 elem = &mmal_vc_payload_list.list[i];
135 break;
136 }
137 vcos_mutex_unlock(&mmal_vc_payload_list.lock);
138
139 return elem;
140}
141
142/** Initialise the shared memory system */
143MMAL_STATUS_T mmal_vc_shm_init(void)
144{
145 MMAL_STATUS_T ret = MMAL_SUCCESS;
146 vcos_once(&once, mmal_vc_shm_init_once);
147
148 vcos_mutex_lock(&refcount_lock);
149 mmal_vc_shm_initialised++;
150 if (mmal_vc_shm_initialised > 1)
151 goto unlock;
152
153#ifdef ENABLE_MMAL_VCSM
154 if (vcsm_init() != 0)
155 {
156 LOG_ERROR("could not initialize vc shared memory service");
157 ret = MMAL_EIO;
158 goto unlock;
159 }
160#endif /* ENABLE_MMAL_VCSM */
161
162 mmal_vc_payload_list_init();
163unlock:
164 vcos_mutex_unlock(&refcount_lock);
165 return ret;
166}
167
168void mmal_vc_shm_exit(void)
169{
170 if (mmal_vc_shm_initialised <= 0)
171 goto unlock;
172
173 mmal_vc_shm_initialised--;
174 if (mmal_vc_shm_initialised != 0)
175 goto unlock;
176
177#ifdef ENABLE_MMAL_VCSM
178 vcsm_exit();
179#endif
180
181 mmal_vc_payload_list_exit();
182unlock:
183 vcos_mutex_unlock(&refcount_lock);
184}
185
186/** Allocate a shared memory buffer */
187uint8_t *mmal_vc_shm_alloc(uint32_t size)
188{
189 uint8_t *mem = NULL;
190
191 MMAL_VC_PAYLOAD_ELEM_T *payload_elem = mmal_vc_payload_list_get();
192 if (!payload_elem)
193 {
194 LOG_ERROR("could not get a free slot in the payload list");
195 return NULL;
196 }
197
198#ifdef ENABLE_MMAL_VCSM
199 unsigned int vcsm_handle = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST, "mmal_vc_port buffer");
200 unsigned int vc_handle = vcsm_vc_hdl_from_hdl(vcsm_handle);
201 mem = (uint8_t *)vcsm_lock( vcsm_handle );
202 if (!mem || !vc_handle)
203 {
204 LOG_ERROR("could not allocate %i bytes of shared memory (handle %x) - mem %p, vc_hdl %08X",
205 (int)size, vcsm_handle, mem, vc_handle);
206 if (mem)
207 vcsm_unlock_hdl(vcsm_handle);
208 if (vcsm_handle)
209 vcsm_free(vcsm_handle);
210 mmal_vc_payload_list_release(payload_elem);
211 return NULL;
212 }
213
214 /* The memory area is automatically mem-locked by vcsm's fault
215 * handler when it is next used. So leave it unlocked until it
216 * is needed.
217 */
218 vcsm_unlock_hdl(vcsm_handle);
219
220 payload_elem->mem = mem;
221 payload_elem->handle = (void *)(intptr_t)vcsm_handle;
222 payload_elem->vc_handle = (void *)(intptr_t)vc_handle;
223#else /* ENABLE_MMAL_VCSM */
224 MMAL_PARAM_UNUSED(size);
225 mmal_vc_payload_list_release(payload_elem);
226#endif /* ENABLE_MMAL_VCSM */
227
228 return mem;
229}
230
231/** Free a shared memory buffer */
232MMAL_STATUS_T mmal_vc_shm_free(uint8_t *mem)
233{
234 MMAL_VC_PAYLOAD_ELEM_T *payload_elem = mmal_vc_payload_list_find_mem(mem);
235 if (payload_elem)
236 {
237#ifdef ENABLE_MMAL_VCSM
238 vcsm_free((uintptr_t)payload_elem->handle);
239#endif /* ENABLE_MMAL_VCSM */
240 mmal_vc_payload_list_release(payload_elem);
241 return MMAL_SUCCESS;
242 }
243
244 return MMAL_EINVAL;
245}
246
247/** Lock a shared memory buffer */
248uint8_t *mmal_vc_shm_lock(uint8_t *mem, uint32_t workaround)
249{
250 /* Zero copy stuff */
251 MMAL_VC_PAYLOAD_ELEM_T *elem = mmal_vc_payload_list_find_handle(mem);
252 MMAL_PARAM_UNUSED(workaround);
253
254 if (elem) {
255 mem = elem->mem;
256#ifdef ENABLE_MMAL_VCSM
257 void *p = vcsm_lock((uintptr_t)elem->handle);
258 if (!p)
259 assert(0);
260#endif /* ENABLE_MMAL_VCSM */
261 }
262
263 return mem;
264}
265
266/** Unlock a shared memory buffer */
267uint8_t *mmal_vc_shm_unlock(uint8_t *mem, uint32_t *length, uint32_t workaround)
268{
269 /* Zero copy stuff */
270 MMAL_VC_PAYLOAD_ELEM_T *elem = mmal_vc_payload_list_find_mem(mem);
271 MMAL_PARAM_UNUSED(workaround);
272
273 if (elem)
274 {
275 *length = 0;
276 mem = (uint8_t *)elem->vc_handle;
277#ifdef ENABLE_MMAL_VCSM
278 vcsm_unlock_ptr(elem->mem);
279#endif /* ENABLE_MMAL_VCSM */
280 }
281
282 return mem;
283}
284