xref: /freebsd/sys/contrib/vchiq/interface/vchiq_arm/vchiq_kern_lib.c (revision b626f5a73a48f44a31a200291b141e1da408a2ff)
1 /**
2  * Copyright (c) 2010-2012 Broadcom. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions, and the following disclaimer,
9  *    without modification.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The names of the above-listed copyright holders may not be used
14  *    to endorse or promote products derived from this software without
15  *    specific prior written permission.
16  *
17  * ALTERNATIVELY, this software may be distributed under the terms of the
18  * GNU General Public License ("GPL") version 2, as published by the Free
19  * Software Foundation.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /* ---- Include Files ---------------------------------------------------- */
35 
36 #include "vchiq_core.h"
37 #include "vchiq_arm.h"
38 #include "vchiq_killable.h"
39 
40 /* ---- Public Variables ------------------------------------------------- */
41 
42 /* ---- Private Constants and Types -------------------------------------- */
43 
44 struct bulk_waiter_node {
45 	struct bulk_waiter bulk_waiter;
46 	int pid;
47 	struct list_head list;
48 };
49 
50 struct vchiq_instance_struct {
51 	VCHIQ_STATE_T *state;
52 
53 	int connected;
54 
55 	struct list_head bulk_waiter_list;
56 	struct mutex bulk_waiter_list_mutex;
57 };
58 
59 static VCHIQ_STATUS_T
60 vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
61 	unsigned int size, VCHIQ_BULK_DIR_T dir);
62 
63 /****************************************************************************
64 *
65 *   vchiq_initialise
66 *
67 ***************************************************************************/
68 #define VCHIQ_INIT_RETRIES 10
vchiq_initialise(VCHIQ_INSTANCE_T * instanceOut)69 VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut)
70 {
71 	VCHIQ_STATUS_T status = VCHIQ_ERROR;
72 	VCHIQ_STATE_T *state;
73 	VCHIQ_INSTANCE_T instance = NULL;
74         int i;
75 
76 	vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
77 
78         /* VideoCore may not be ready due to boot up timing.
79            It may never be ready if kernel and firmware are mismatched, so don't block forever. */
80         for (i=0; i<VCHIQ_INIT_RETRIES; i++) {
81 		state = vchiq_get_state();
82 		if (state)
83 			break;
84 		udelay(500);
85 	}
86 	if (i==VCHIQ_INIT_RETRIES) {
87 		vchiq_log_error(vchiq_core_log_level,
88 			"%s: videocore not initialized\n", __func__);
89 		goto failed;
90 	} else if (i>0) {
91 		vchiq_log_warning(vchiq_core_log_level,
92 			"%s: videocore initialized after %d retries\n", __func__, i);
93 	}
94 
95 	instance = kzalloc(sizeof(*instance), GFP_KERNEL);
96 	if (!instance) {
97 		vchiq_log_error(vchiq_core_log_level,
98 			"%s: error allocating vchiq instance\n", __func__);
99 		goto failed;
100 	}
101 
102 	instance->connected = 0;
103 	instance->state = state;
104 	lmutex_init(&instance->bulk_waiter_list_mutex);
105 	INIT_LIST_HEAD(&instance->bulk_waiter_list);
106 
107 	*instanceOut = instance;
108 
109 	status = VCHIQ_SUCCESS;
110 
111 failed:
112 	vchiq_log_trace(vchiq_core_log_level,
113 		"%s(%p): returning %d", __func__, instance, status);
114 
115 	return status;
116 }
117 EXPORT_SYMBOL(vchiq_initialise);
118 
119 /****************************************************************************
120 *
121 *   vchiq_shutdown
122 *
123 ***************************************************************************/
124 
vchiq_shutdown(VCHIQ_INSTANCE_T instance)125 VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
126 {
127 	VCHIQ_STATUS_T status;
128 	VCHIQ_STATE_T *state = instance->state;
129 
130 	vchiq_log_trace(vchiq_core_log_level,
131 		"%s(%p) called", __func__, instance);
132 
133 	if (lmutex_lock_interruptible(&state->mutex) != 0)
134 		return VCHIQ_RETRY;
135 
136 	/* Remove all services */
137 	status = vchiq_shutdown_internal(state, instance);
138 
139 	lmutex_unlock(&state->mutex);
140 
141 	vchiq_log_trace(vchiq_core_log_level,
142 		"%s(%p): returning %d", __func__, instance, status);
143 
144 	if (status == VCHIQ_SUCCESS) {
145 		struct list_head *pos, *next;
146 		list_for_each_safe(pos, next,
147 				&instance->bulk_waiter_list) {
148 			struct bulk_waiter_node *waiter;
149 			waiter = list_entry(pos,
150 					struct bulk_waiter_node,
151 					list);
152 			list_del(pos);
153 			vchiq_log_info(vchiq_arm_log_level,
154 					"bulk_waiter - cleaned up %x "
155 					"for pid %d",
156 					(unsigned int)waiter, waiter->pid);
157 			_sema_destroy(&waiter->bulk_waiter.event);
158 
159 			kfree(waiter);
160 		}
161 
162 		lmutex_destroy(&instance->bulk_waiter_list_mutex);
163 
164 		kfree(instance);
165 	}
166 
167 	return status;
168 }
169 EXPORT_SYMBOL(vchiq_shutdown);
170 
171 /****************************************************************************
172 *
173 *   vchiq_is_connected
174 *
175 ***************************************************************************/
176 
vchiq_is_connected(VCHIQ_INSTANCE_T instance)177 static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
178 {
179 	return instance->connected;
180 }
181 
182 /****************************************************************************
183 *
184 *   vchiq_connect
185 *
186 ***************************************************************************/
187 
vchiq_connect(VCHIQ_INSTANCE_T instance)188 VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
189 {
190 	VCHIQ_STATUS_T status;
191 	VCHIQ_STATE_T *state = instance->state;
192 
193 	vchiq_log_trace(vchiq_core_log_level,
194 		"%s(%p) called", __func__, instance);
195 
196 	if (lmutex_lock_interruptible(&state->mutex) != 0) {
197 		vchiq_log_trace(vchiq_core_log_level,
198 			"%s: call to lmutex_lock failed", __func__);
199 		status = VCHIQ_RETRY;
200 		goto failed;
201 	}
202 	status = vchiq_connect_internal(state, instance);
203 
204 	if (status == VCHIQ_SUCCESS)
205 		instance->connected = 1;
206 
207 	lmutex_unlock(&state->mutex);
208 
209 failed:
210 	vchiq_log_trace(vchiq_core_log_level,
211 		"%s(%p): returning %d", __func__, instance, status);
212 
213 	return status;
214 }
215 EXPORT_SYMBOL(vchiq_connect);
216 
217 /****************************************************************************
218 *
219 *   vchiq_add_service
220 *
221 ***************************************************************************/
222 
vchiq_add_service(VCHIQ_INSTANCE_T instance,const VCHIQ_SERVICE_PARAMS_T * params,VCHIQ_SERVICE_HANDLE_T * phandle)223 VCHIQ_STATUS_T vchiq_add_service(
224 	VCHIQ_INSTANCE_T              instance,
225 	const VCHIQ_SERVICE_PARAMS_T *params,
226 	VCHIQ_SERVICE_HANDLE_T       *phandle)
227 {
228 	VCHIQ_STATUS_T status;
229 	VCHIQ_STATE_T *state = instance->state;
230 	VCHIQ_SERVICE_T *service = NULL;
231 	int srvstate;
232 
233 	vchiq_log_trace(vchiq_core_log_level,
234 		"%s(%p) called", __func__, instance);
235 
236 	*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
237 
238 	srvstate = vchiq_is_connected(instance)
239 		? VCHIQ_SRVSTATE_LISTENING
240 		: VCHIQ_SRVSTATE_HIDDEN;
241 
242 	service = vchiq_add_service_internal(
243 		state,
244 		params,
245 		srvstate,
246 		instance,
247 		NULL);
248 
249 	if (service) {
250 		*phandle = service->handle;
251 		status = VCHIQ_SUCCESS;
252 	} else
253 		status = VCHIQ_ERROR;
254 
255 	vchiq_log_trace(vchiq_core_log_level,
256 		"%s(%p): returning %d", __func__, instance, status);
257 
258 	return status;
259 }
260 EXPORT_SYMBOL(vchiq_add_service);
261 
262 /****************************************************************************
263 *
264 *   vchiq_open_service
265 *
266 ***************************************************************************/
267 
vchiq_open_service(VCHIQ_INSTANCE_T instance,const VCHIQ_SERVICE_PARAMS_T * params,VCHIQ_SERVICE_HANDLE_T * phandle)268 VCHIQ_STATUS_T vchiq_open_service(
269 	VCHIQ_INSTANCE_T              instance,
270 	const VCHIQ_SERVICE_PARAMS_T *params,
271 	VCHIQ_SERVICE_HANDLE_T       *phandle)
272 {
273 	VCHIQ_STATUS_T   status = VCHIQ_ERROR;
274 	VCHIQ_STATE_T   *state = instance->state;
275 	VCHIQ_SERVICE_T *service = NULL;
276 
277 	vchiq_log_trace(vchiq_core_log_level,
278 		"%s(%p) called", __func__, instance);
279 
280 	*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
281 
282 	if (!vchiq_is_connected(instance))
283 		goto failed;
284 
285 	service = vchiq_add_service_internal(state,
286 		params,
287 		VCHIQ_SRVSTATE_OPENING,
288 		instance,
289 		NULL);
290 
291 	if (service) {
292 		*phandle = service->handle;
293 		status = vchiq_open_service_internal(service,
294 		    (uintptr_t)current);
295 		if (status != VCHIQ_SUCCESS) {
296 			vchiq_remove_service(service->handle);
297 			*phandle = VCHIQ_SERVICE_HANDLE_INVALID;
298 		}
299 	}
300 
301 failed:
302 	vchiq_log_trace(vchiq_core_log_level,
303 		"%s(%p): returning %d", __func__, instance, status);
304 
305 	return status;
306 }
307 EXPORT_SYMBOL(vchiq_open_service);
308 
309 VCHIQ_STATUS_T
vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata)310 vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
311 	void *data, unsigned int size, void *userdata)
312 {
313 	return vchiq_bulk_transfer(handle,
314 		VCHI_MEM_HANDLE_INVALID, data, size, userdata,
315 		VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
316 }
317 EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
318 
319 VCHIQ_STATUS_T
vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata)320 vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
321 	unsigned int size, void *userdata)
322 {
323 	return vchiq_bulk_transfer(handle,
324 		VCHI_MEM_HANDLE_INVALID, data, size, userdata,
325 		VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
326 }
327 EXPORT_SYMBOL(vchiq_queue_bulk_receive);
328 
329 VCHIQ_STATUS_T
vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata,VCHIQ_BULK_MODE_T mode)330 vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, void *data,
331 	unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
332 {
333 	VCHIQ_STATUS_T status;
334 
335 	switch (mode) {
336 	case VCHIQ_BULK_MODE_NOCALLBACK:
337 	case VCHIQ_BULK_MODE_CALLBACK:
338 		status = vchiq_bulk_transfer(handle,
339 			VCHI_MEM_HANDLE_INVALID, data, size, userdata,
340 			mode, VCHIQ_BULK_TRANSMIT);
341 		break;
342 	case VCHIQ_BULK_MODE_BLOCKING:
343 		status = vchiq_blocking_bulk_transfer(handle,
344 			data, size, VCHIQ_BULK_TRANSMIT);
345 		break;
346 	default:
347 		return VCHIQ_ERROR;
348 	}
349 
350 	return status;
351 }
352 EXPORT_SYMBOL(vchiq_bulk_transmit);
353 
354 VCHIQ_STATUS_T
vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata,VCHIQ_BULK_MODE_T mode)355 vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
356 	unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
357 {
358 	VCHIQ_STATUS_T status;
359 
360 	switch (mode) {
361 	case VCHIQ_BULK_MODE_NOCALLBACK:
362 	case VCHIQ_BULK_MODE_CALLBACK:
363 		status = vchiq_bulk_transfer(handle,
364 			VCHI_MEM_HANDLE_INVALID, data, size, userdata,
365 			mode, VCHIQ_BULK_RECEIVE);
366 		break;
367 	case VCHIQ_BULK_MODE_BLOCKING:
368 		status = vchiq_blocking_bulk_transfer(handle,
369 			data, size, VCHIQ_BULK_RECEIVE);
370 		break;
371 	default:
372 		return VCHIQ_ERROR;
373 	}
374 
375 	return status;
376 }
377 EXPORT_SYMBOL(vchiq_bulk_receive);
378 
379 static VCHIQ_STATUS_T
vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,VCHIQ_BULK_DIR_T dir)380 vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
381 	unsigned int size, VCHIQ_BULK_DIR_T dir)
382 {
383 	VCHIQ_INSTANCE_T instance;
384 	VCHIQ_SERVICE_T *service;
385 	VCHIQ_STATUS_T status;
386 	struct bulk_waiter_node *waiter = NULL;
387 	struct list_head *pos;
388 
389 	service = find_service_by_handle(handle);
390 	if (!service)
391 		return VCHIQ_ERROR;
392 
393 	instance = service->instance;
394 
395 	unlock_service(service);
396 
397 	lmutex_lock(&instance->bulk_waiter_list_mutex);
398 	list_for_each(pos, &instance->bulk_waiter_list) {
399 		if (list_entry(pos, struct bulk_waiter_node,
400 				list)->pid == current->p_pid) {
401 			waiter = list_entry(pos,
402 				struct bulk_waiter_node,
403 				list);
404 			list_del(pos);
405 			break;
406 		}
407 	}
408 	lmutex_unlock(&instance->bulk_waiter_list_mutex);
409 
410 	if (waiter) {
411 		VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
412 		if (bulk) {
413 			/* This thread has an outstanding bulk transfer. */
414 			if ((bulk->data != data) ||
415 				(bulk->size != size)) {
416 				/* This is not a retry of the previous one.
417 				** Cancel the signal when the transfer
418 				** completes. */
419 				spin_lock(&bulk_waiter_spinlock);
420 				bulk->userdata = NULL;
421 				spin_unlock(&bulk_waiter_spinlock);
422 			}
423 		}
424 	}
425 
426 	if (!waiter) {
427 		waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
428 		if (!waiter) {
429 			vchiq_log_error(vchiq_core_log_level,
430 				"%s - out of memory", __func__);
431 			return VCHIQ_ERROR;
432 		}
433 	}
434 
435 	status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
436 		data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
437 		dir);
438 	if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
439 		!waiter->bulk_waiter.bulk) {
440 		VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
441 		if (bulk) {
442 			/* Cancel the signal when the transfer
443 			 ** completes. */
444 			spin_lock(&bulk_waiter_spinlock);
445 			bulk->userdata = NULL;
446 			spin_unlock(&bulk_waiter_spinlock);
447 		}
448 		_sema_destroy(&waiter->bulk_waiter.event);
449 
450 		kfree(waiter);
451 	} else {
452 		waiter->pid = current->p_pid;
453 		lmutex_lock(&instance->bulk_waiter_list_mutex);
454 		list_add(&waiter->list, &instance->bulk_waiter_list);
455 		lmutex_unlock(&instance->bulk_waiter_list_mutex);
456 		vchiq_log_info(vchiq_arm_log_level,
457 				"saved bulk_waiter %x for pid %d",
458 				(unsigned int)waiter, current->p_pid);
459 	}
460 
461 	return status;
462 }
463