Deleted Added
full compact
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
44struct bulk_waiter_node {
45 struct bulk_waiter bulk_waiter;
46 int pid;
47 struct list_head list;
48};
49
50struct 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
59static VCHIQ_STATUS_T
60vchiq_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
69VCHIQ_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
111failed:
112 vchiq_log_trace(vchiq_core_log_level,
113 "%s(%p): returning %d", __func__, instance, status);
114
115 return status;
116}
117EXPORT_SYMBOL(vchiq_initialise);
118
119/****************************************************************************
120*
121* vchiq_shutdown
122*
123***************************************************************************/
124
125VCHIQ_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}
169EXPORT_SYMBOL(vchiq_shutdown);
170
171/****************************************************************************
172*
173* vchiq_is_connected
174*
175***************************************************************************/
176
177static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
178{
179 return instance->connected;
180}
181
182/****************************************************************************
183*
184* vchiq_connect
185*
186***************************************************************************/
187
188VCHIQ_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
209failed:
210 vchiq_log_trace(vchiq_core_log_level,
211 "%s(%p): returning %d", __func__, instance, status);
212
213 return status;
214}
215EXPORT_SYMBOL(vchiq_connect);
216
217/****************************************************************************
218*
219* vchiq_add_service
220*
221***************************************************************************/
222
223VCHIQ_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}
260EXPORT_SYMBOL(vchiq_add_service);
261
262/****************************************************************************
263*
264* vchiq_open_service
265*
266***************************************************************************/
267
268VCHIQ_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
301failed:
302 vchiq_log_trace(vchiq_core_log_level,
303 "%s(%p): returning %d", __func__, instance, status);
304
305 return status;
306}
307EXPORT_SYMBOL(vchiq_open_service);
308
309VCHIQ_STATUS_T
310vchiq_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}
317EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
318
319VCHIQ_STATUS_T
320vchiq_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}
327EXPORT_SYMBOL(vchiq_queue_bulk_receive);
328
329VCHIQ_STATUS_T
330vchiq_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}
352EXPORT_SYMBOL(vchiq_bulk_transmit);
353
354VCHIQ_STATUS_T
355vchiq_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}
377EXPORT_SYMBOL(vchiq_bulk_receive);
378
379static VCHIQ_STATUS_T
380vchiq_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}