Deleted Added
sdiff udiff text old ( 278277 ) new ( 290245 )
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
39/* ---- Public Variables ------------------------------------------------- */
40
41/* ---- Private Constants and Types -------------------------------------- */
42
43struct bulk_waiter_node {
44 struct bulk_waiter bulk_waiter;
45 int pid;
46 struct list_head list;
47};
48
49struct vchiq_instance_struct {
50 VCHIQ_STATE_T *state;
51
52 int connected;
53
54 struct list_head bulk_waiter_list;
55 struct mutex bulk_waiter_list_mutex;
56};
57
58static VCHIQ_STATUS_T
59vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
60 unsigned int size, VCHIQ_BULK_DIR_T dir);
61
62/****************************************************************************
63*
64* vchiq_initialise
65*
66***************************************************************************/
67#define VCHIQ_INIT_RETRIES 10
68VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut)
69{
70 VCHIQ_STATUS_T status = VCHIQ_ERROR;
71 VCHIQ_STATE_T *state;
72 VCHIQ_INSTANCE_T instance = NULL;
73 int i;
74
75 vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
76
77 /* VideoCore may not be ready due to boot up timing.
78 It may never be ready if kernel and firmware are mismatched, so don't block forever. */
79 for (i=0; i<VCHIQ_INIT_RETRIES; i++) {
80 state = vchiq_get_state();
81 if (state)
82 break;
83 udelay(500);
84 }
85 if (i==VCHIQ_INIT_RETRIES) {
86 vchiq_log_error(vchiq_core_log_level,
87 "%s: videocore not initialized\n", __func__);
88 goto failed;
89 } else if (i>0) {
90 vchiq_log_warning(vchiq_core_log_level,
91 "%s: videocore initialized after %d retries\n", __func__, i);
92 }
93
94 instance = kzalloc(sizeof(*instance), GFP_KERNEL);
95 if (!instance) {
96 vchiq_log_error(vchiq_core_log_level,
97 "%s: error allocating vchiq instance\n", __func__);
98 goto failed;
99 }
100
101 instance->connected = 0;
102 instance->state = state;
103 lmutex_init(&instance->bulk_waiter_list_mutex);
104 INIT_LIST_HEAD(&instance->bulk_waiter_list);
105
106 *instanceOut = instance;
107
108 status = VCHIQ_SUCCESS;
109
110failed:
111 vchiq_log_trace(vchiq_core_log_level,
112 "%s(%p): returning %d", __func__, instance, status);
113
114 return status;
115}
116EXPORT_SYMBOL(vchiq_initialise);
117
118/****************************************************************************
119*
120* vchiq_shutdown
121*
122***************************************************************************/
123
124VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
125{
126 VCHIQ_STATUS_T status;
127 VCHIQ_STATE_T *state = instance->state;
128
129 vchiq_log_trace(vchiq_core_log_level,
130 "%s(%p) called", __func__, instance);
131
132 if (lmutex_lock_interruptible(&state->mutex) != 0)
133 return VCHIQ_RETRY;
134
135 /* Remove all services */
136 status = vchiq_shutdown_internal(state, instance);
137
138 lmutex_unlock(&state->mutex);
139
140 vchiq_log_trace(vchiq_core_log_level,
141 "%s(%p): returning %d", __func__, instance, status);
142
143 if (status == VCHIQ_SUCCESS) {
144 struct list_head *pos, *next;
145 list_for_each_safe(pos, next,
146 &instance->bulk_waiter_list) {
147 struct bulk_waiter_node *waiter;
148 waiter = list_entry(pos,
149 struct bulk_waiter_node,
150 list);
151 list_del(pos);
152 vchiq_log_info(vchiq_arm_log_level,
153 "bulk_waiter - cleaned up %x "
154 "for pid %d",
155 (unsigned int)waiter, waiter->pid);
156 _sema_destroy(&waiter->bulk_waiter.event);
157
158 kfree(waiter);
159 }
160
161 lmutex_destroy(&instance->bulk_waiter_list_mutex);
162
163 kfree(instance);
164 }
165
166 return status;
167}
168EXPORT_SYMBOL(vchiq_shutdown);
169
170/****************************************************************************
171*
172* vchiq_is_connected
173*
174***************************************************************************/
175
176static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
177{
178 return instance->connected;
179}
180
181/****************************************************************************
182*
183* vchiq_connect
184*
185***************************************************************************/
186
187VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance)
188{
189 VCHIQ_STATUS_T status;
190 VCHIQ_STATE_T *state = instance->state;
191
192 vchiq_log_trace(vchiq_core_log_level,
193 "%s(%p) called", __func__, instance);
194
195 if (lmutex_lock_interruptible(&state->mutex) != 0) {
196 vchiq_log_trace(vchiq_core_log_level,
197 "%s: call to lmutex_lock failed", __func__);
198 status = VCHIQ_RETRY;
199 goto failed;
200 }
201 status = vchiq_connect_internal(state, instance);
202
203 if (status == VCHIQ_SUCCESS)
204 instance->connected = 1;
205
206 lmutex_unlock(&state->mutex);
207
208failed:
209 vchiq_log_trace(vchiq_core_log_level,
210 "%s(%p): returning %d", __func__, instance, status);
211
212 return status;
213}
214EXPORT_SYMBOL(vchiq_connect);
215
216/****************************************************************************
217*
218* vchiq_add_service
219*
220***************************************************************************/
221
222VCHIQ_STATUS_T vchiq_add_service(
223 VCHIQ_INSTANCE_T instance,
224 const VCHIQ_SERVICE_PARAMS_T *params,
225 VCHIQ_SERVICE_HANDLE_T *phandle)
226{
227 VCHIQ_STATUS_T status;
228 VCHIQ_STATE_T *state = instance->state;
229 VCHIQ_SERVICE_T *service = NULL;
230 int srvstate;
231
232 vchiq_log_trace(vchiq_core_log_level,
233 "%s(%p) called", __func__, instance);
234
235 *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
236
237 srvstate = vchiq_is_connected(instance)
238 ? VCHIQ_SRVSTATE_LISTENING
239 : VCHIQ_SRVSTATE_HIDDEN;
240
241 service = vchiq_add_service_internal(
242 state,
243 params,
244 srvstate,
245 instance,
246 NULL);
247
248 if (service) {
249 *phandle = service->handle;
250 status = VCHIQ_SUCCESS;
251 } else
252 status = VCHIQ_ERROR;
253
254 vchiq_log_trace(vchiq_core_log_level,
255 "%s(%p): returning %d", __func__, instance, status);
256
257 return status;
258}
259EXPORT_SYMBOL(vchiq_add_service);
260
261/****************************************************************************
262*
263* vchiq_open_service
264*
265***************************************************************************/
266
267VCHIQ_STATUS_T vchiq_open_service(
268 VCHIQ_INSTANCE_T instance,
269 const VCHIQ_SERVICE_PARAMS_T *params,
270 VCHIQ_SERVICE_HANDLE_T *phandle)
271{
272 VCHIQ_STATUS_T status = VCHIQ_ERROR;
273 VCHIQ_STATE_T *state = instance->state;
274 VCHIQ_SERVICE_T *service = NULL;
275
276 vchiq_log_trace(vchiq_core_log_level,
277 "%s(%p) called", __func__, instance);
278
279 *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
280
281 if (!vchiq_is_connected(instance))
282 goto failed;
283
284 service = vchiq_add_service_internal(state,
285 params,
286 VCHIQ_SRVSTATE_OPENING,
287 instance,
288 NULL);
289
290 if (service) {
291 *phandle = service->handle;
292 status = vchiq_open_service_internal(service,
293 (uintptr_t)current);
294 if (status != VCHIQ_SUCCESS) {
295 vchiq_remove_service(service->handle);
296 *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
297 }
298 }
299
300failed:
301 vchiq_log_trace(vchiq_core_log_level,
302 "%s(%p): returning %d", __func__, instance, status);
303
304 return status;
305}
306EXPORT_SYMBOL(vchiq_open_service);
307
308VCHIQ_STATUS_T
309vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
310 void *data, unsigned int size, void *userdata)
311{
312 return vchiq_bulk_transfer(handle,
313 VCHI_MEM_HANDLE_INVALID, data, size, userdata,
314 VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
315}
316EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
317
318VCHIQ_STATUS_T
319vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
320 unsigned int size, void *userdata)
321{
322 return vchiq_bulk_transfer(handle,
323 VCHI_MEM_HANDLE_INVALID, data, size, userdata,
324 VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
325}
326EXPORT_SYMBOL(vchiq_queue_bulk_receive);
327
328VCHIQ_STATUS_T
329vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, void *data,
330 unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
331{
332 VCHIQ_STATUS_T status;
333
334 switch (mode) {
335 case VCHIQ_BULK_MODE_NOCALLBACK:
336 case VCHIQ_BULK_MODE_CALLBACK:
337 status = vchiq_bulk_transfer(handle,
338 VCHI_MEM_HANDLE_INVALID, data, size, userdata,
339 mode, VCHIQ_BULK_TRANSMIT);
340 break;
341 case VCHIQ_BULK_MODE_BLOCKING:
342 status = vchiq_blocking_bulk_transfer(handle,
343 data, size, VCHIQ_BULK_TRANSMIT);
344 break;
345 default:
346 return VCHIQ_ERROR;
347 }
348
349 return status;
350}
351EXPORT_SYMBOL(vchiq_bulk_transmit);
352
353VCHIQ_STATUS_T
354vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
355 unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
356{
357 VCHIQ_STATUS_T status;
358
359 switch (mode) {
360 case VCHIQ_BULK_MODE_NOCALLBACK:
361 case VCHIQ_BULK_MODE_CALLBACK:
362 status = vchiq_bulk_transfer(handle,
363 VCHI_MEM_HANDLE_INVALID, data, size, userdata,
364 mode, VCHIQ_BULK_RECEIVE);
365 break;
366 case VCHIQ_BULK_MODE_BLOCKING:
367 status = vchiq_blocking_bulk_transfer(handle,
368 data, size, VCHIQ_BULK_RECEIVE);
369 break;
370 default:
371 return VCHIQ_ERROR;
372 }
373
374 return status;
375}
376EXPORT_SYMBOL(vchiq_bulk_receive);
377
378static VCHIQ_STATUS_T
379vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
380 unsigned int size, VCHIQ_BULK_DIR_T dir)
381{
382 VCHIQ_INSTANCE_T instance;
383 VCHIQ_SERVICE_T *service;
384 VCHIQ_STATUS_T status;
385 struct bulk_waiter_node *waiter = NULL;
386 struct list_head *pos;
387
388 service = find_service_by_handle(handle);
389 if (!service)
390 return VCHIQ_ERROR;
391
392 instance = service->instance;
393
394 unlock_service(service);
395
396 lmutex_lock(&instance->bulk_waiter_list_mutex);
397 list_for_each(pos, &instance->bulk_waiter_list) {
398 if (list_entry(pos, struct bulk_waiter_node,
399 list)->pid == current->p_pid) {
400 waiter = list_entry(pos,
401 struct bulk_waiter_node,
402 list);
403 list_del(pos);
404 break;
405 }
406 }
407 lmutex_unlock(&instance->bulk_waiter_list_mutex);
408
409 if (waiter) {
410 VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
411 if (bulk) {
412 /* This thread has an outstanding bulk transfer. */
413 if ((bulk->data != data) ||
414 (bulk->size != size)) {
415 /* This is not a retry of the previous one.
416 ** Cancel the signal when the transfer
417 ** completes. */
418 spin_lock(&bulk_waiter_spinlock);
419 bulk->userdata = NULL;
420 spin_unlock(&bulk_waiter_spinlock);
421 }
422 }
423 }
424
425 if (!waiter) {
426 waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
427 if (!waiter) {
428 vchiq_log_error(vchiq_core_log_level,
429 "%s - out of memory", __func__);
430 return VCHIQ_ERROR;
431 }
432 }
433
434 status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
435 data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
436 dir);
437 if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
438 !waiter->bulk_waiter.bulk) {
439 VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
440 if (bulk) {
441 /* Cancel the signal when the transfer
442 ** completes. */
443 spin_lock(&bulk_waiter_spinlock);
444 bulk->userdata = NULL;
445 spin_unlock(&bulk_waiter_spinlock);
446 }
447 _sema_destroy(&waiter->bulk_waiter.event);
448
449 kfree(waiter);
450 } else {
451 waiter->pid = current->p_pid;
452 lmutex_lock(&instance->bulk_waiter_list_mutex);
453 list_add(&waiter->list, &instance->bulk_waiter_list);
454 lmutex_unlock(&instance->bulk_waiter_list_mutex);
455 vchiq_log_info(vchiq_arm_log_level,
456 "saved bulk_waiter %x for pid %d",
457 (unsigned int)waiter, current->p_pid);
458 }
459
460 return status;
461}