GNU Linux-libre 4.9.309-gnu1
[releases.git] / drivers / staging / vc04_services / interface / vchiq_arm / vchiq_kern_lib.c
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 <linux/kernel.h>
37 #include <linux/module.h>
38 #include <linux/mutex.h>
39
40 #include "vchiq_core.h"
41 #include "vchiq_arm.h"
42 #include "vchiq_killable.h"
43
44 /* ---- Public Variables ------------------------------------------------- */
45
46 /* ---- Private Constants and Types -------------------------------------- */
47
48 struct bulk_waiter_node {
49         struct bulk_waiter bulk_waiter;
50         int pid;
51         struct list_head list;
52 };
53
54 struct vchiq_instance_struct {
55         VCHIQ_STATE_T *state;
56
57         int connected;
58
59         struct list_head bulk_waiter_list;
60         struct mutex bulk_waiter_list_mutex;
61 };
62
63 static VCHIQ_STATUS_T
64 vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
65         unsigned int size, VCHIQ_BULK_DIR_T dir);
66
67 /****************************************************************************
68 *
69 *   vchiq_initialise
70 *
71 ***************************************************************************/
72 #define VCHIQ_INIT_RETRIES 10
73 VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut)
74 {
75         VCHIQ_STATUS_T status = VCHIQ_ERROR;
76         VCHIQ_STATE_T *state;
77         VCHIQ_INSTANCE_T instance = NULL;
78         int i;
79
80         vchiq_log_trace(vchiq_core_log_level, "%s called", __func__);
81
82         /* VideoCore may not be ready due to boot up timing.
83            It may never be ready if kernel and firmware are mismatched, so don't block forever. */
84         for (i=0; i<VCHIQ_INIT_RETRIES; i++) {
85                 state = vchiq_get_state();
86                 if (state)
87                         break;
88                 udelay(500);
89         }
90         if (i==VCHIQ_INIT_RETRIES) {
91                 vchiq_log_error(vchiq_core_log_level,
92                         "%s: videocore not initialized\n", __func__);
93                 goto failed;
94         } else if (i>0) {
95                 vchiq_log_warning(vchiq_core_log_level,
96                         "%s: videocore initialized after %d retries\n", __func__, i);
97         }
98
99         instance = kzalloc(sizeof(*instance), GFP_KERNEL);
100         if (!instance) {
101                 vchiq_log_error(vchiq_core_log_level,
102                         "%s: error allocating vchiq instance\n", __func__);
103                 goto failed;
104         }
105
106         instance->connected = 0;
107         instance->state = state;
108         mutex_init(&instance->bulk_waiter_list_mutex);
109         INIT_LIST_HEAD(&instance->bulk_waiter_list);
110
111         *instanceOut = instance;
112
113         status = VCHIQ_SUCCESS;
114
115 failed:
116         vchiq_log_trace(vchiq_core_log_level,
117                 "%s(%p): returning %d", __func__, instance, status);
118
119         return status;
120 }
121 EXPORT_SYMBOL(vchiq_initialise);
122
123 /****************************************************************************
124 *
125 *   vchiq_shutdown
126 *
127 ***************************************************************************/
128
129 VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance)
130 {
131         VCHIQ_STATUS_T status;
132         VCHIQ_STATE_T *state = instance->state;
133
134         vchiq_log_trace(vchiq_core_log_level,
135                 "%s(%p) called", __func__, instance);
136
137         if (mutex_lock_interruptible(&state->mutex) != 0)
138                 return VCHIQ_RETRY;
139
140         /* Remove all services */
141         status = vchiq_shutdown_internal(state, instance);
142
143         mutex_unlock(&state->mutex);
144
145         vchiq_log_trace(vchiq_core_log_level,
146                 "%s(%p): returning %d", __func__, instance, status);
147
148         if (status == VCHIQ_SUCCESS) {
149                 struct list_head *pos, *next;
150                 list_for_each_safe(pos, next,
151                                 &instance->bulk_waiter_list) {
152                         struct bulk_waiter_node *waiter;
153                         waiter = list_entry(pos,
154                                         struct bulk_waiter_node,
155                                         list);
156                         list_del(pos);
157                         vchiq_log_info(vchiq_arm_log_level,
158                                         "bulk_waiter - cleaned up %x "
159                                         "for pid %d",
160                                         (unsigned int)waiter, waiter->pid);
161                         kfree(waiter);
162                 }
163                 kfree(instance);
164         }
165
166         return status;
167 }
168 EXPORT_SYMBOL(vchiq_shutdown);
169
170 /****************************************************************************
171 *
172 *   vchiq_is_connected
173 *
174 ***************************************************************************/
175
176 int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
177 {
178         return instance->connected;
179 }
180
181 /****************************************************************************
182 *
183 *   vchiq_connect
184 *
185 ***************************************************************************/
186
187 VCHIQ_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 (mutex_lock_interruptible(&state->mutex) != 0) {
196                 vchiq_log_trace(vchiq_core_log_level,
197                         "%s: call to mutex_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         mutex_unlock(&state->mutex);
207
208 failed:
209         vchiq_log_trace(vchiq_core_log_level,
210                 "%s(%p): returning %d", __func__, instance, status);
211
212         return status;
213 }
214 EXPORT_SYMBOL(vchiq_connect);
215
216 /****************************************************************************
217 *
218 *   vchiq_add_service
219 *
220 ***************************************************************************/
221
222 VCHIQ_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 }
259 EXPORT_SYMBOL(vchiq_add_service);
260
261 /****************************************************************************
262 *
263 *   vchiq_open_service
264 *
265 ***************************************************************************/
266
267 VCHIQ_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, current->pid);
293                 if (status != VCHIQ_SUCCESS) {
294                         vchiq_remove_service(service->handle);
295                         *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
296                 }
297         }
298
299 failed:
300         vchiq_log_trace(vchiq_core_log_level,
301                 "%s(%p): returning %d", __func__, instance, status);
302
303         return status;
304 }
305 EXPORT_SYMBOL(vchiq_open_service);
306
307 VCHIQ_STATUS_T
308 vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
309         const void *data, unsigned int size, void *userdata)
310 {
311         return vchiq_bulk_transfer(handle,
312                 VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
313                 VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
314 }
315 EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
316
317 VCHIQ_STATUS_T
318 vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
319         unsigned int size, void *userdata)
320 {
321         return vchiq_bulk_transfer(handle,
322                 VCHI_MEM_HANDLE_INVALID, data, size, userdata,
323                 VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE);
324 }
325 EXPORT_SYMBOL(vchiq_queue_bulk_receive);
326
327 VCHIQ_STATUS_T
328 vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data,
329         unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
330 {
331         VCHIQ_STATUS_T status;
332
333         switch (mode) {
334         case VCHIQ_BULK_MODE_NOCALLBACK:
335         case VCHIQ_BULK_MODE_CALLBACK:
336                 status = vchiq_bulk_transfer(handle,
337                         VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
338                         mode, VCHIQ_BULK_TRANSMIT);
339                 break;
340         case VCHIQ_BULK_MODE_BLOCKING:
341                 status = vchiq_blocking_bulk_transfer(handle,
342                         (void *)data, size, VCHIQ_BULK_TRANSMIT);
343                 break;
344         default:
345                 return VCHIQ_ERROR;
346         }
347
348         return status;
349 }
350 EXPORT_SYMBOL(vchiq_bulk_transmit);
351
352 VCHIQ_STATUS_T
353 vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data,
354         unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode)
355 {
356         VCHIQ_STATUS_T status;
357
358         switch (mode) {
359         case VCHIQ_BULK_MODE_NOCALLBACK:
360         case VCHIQ_BULK_MODE_CALLBACK:
361                 status = vchiq_bulk_transfer(handle,
362                         VCHI_MEM_HANDLE_INVALID, data, size, userdata,
363                         mode, VCHIQ_BULK_RECEIVE);
364                 break;
365         case VCHIQ_BULK_MODE_BLOCKING:
366                 status = vchiq_blocking_bulk_transfer(handle,
367                         (void *)data, size, VCHIQ_BULK_RECEIVE);
368                 break;
369         default:
370                 return VCHIQ_ERROR;
371         }
372
373         return status;
374 }
375 EXPORT_SYMBOL(vchiq_bulk_receive);
376
377 static VCHIQ_STATUS_T
378 vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data,
379         unsigned int size, VCHIQ_BULK_DIR_T dir)
380 {
381         VCHIQ_INSTANCE_T instance;
382         VCHIQ_SERVICE_T *service;
383         VCHIQ_STATUS_T status;
384         struct bulk_waiter_node *waiter = NULL;
385         struct list_head *pos;
386
387         service = find_service_by_handle(handle);
388         if (!service)
389                 return VCHIQ_ERROR;
390
391         instance = service->instance;
392
393         unlock_service(service);
394
395         mutex_lock(&instance->bulk_waiter_list_mutex);
396         list_for_each(pos, &instance->bulk_waiter_list) {
397                 if (list_entry(pos, struct bulk_waiter_node,
398                                 list)->pid == current->pid) {
399                         waiter = list_entry(pos,
400                                 struct bulk_waiter_node,
401                                 list);
402                         list_del(pos);
403                         break;
404                 }
405         }
406         mutex_unlock(&instance->bulk_waiter_list_mutex);
407
408         if (waiter) {
409                 VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
410                 if (bulk) {
411                         /* This thread has an outstanding bulk transfer. */
412                         if ((bulk->data != data) ||
413                                 (bulk->size != size)) {
414                                 /* This is not a retry of the previous one.
415                                 ** Cancel the signal when the transfer
416                                 ** completes. */
417                                 spin_lock(&bulk_waiter_spinlock);
418                                 bulk->userdata = NULL;
419                                 spin_unlock(&bulk_waiter_spinlock);
420                         }
421                 }
422         }
423
424         if (!waiter) {
425                 waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
426                 if (!waiter) {
427                         vchiq_log_error(vchiq_core_log_level,
428                                 "%s - out of memory", __func__);
429                         return VCHIQ_ERROR;
430                 }
431         }
432
433         status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID,
434                 data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING,
435                 dir);
436         if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
437                 !waiter->bulk_waiter.bulk) {
438                 VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
439                 if (bulk) {
440                         /* Cancel the signal when the transfer
441                          ** completes. */
442                         spin_lock(&bulk_waiter_spinlock);
443                         bulk->userdata = NULL;
444                         spin_unlock(&bulk_waiter_spinlock);
445                 }
446                 kfree(waiter);
447         } else {
448                 waiter->pid = current->pid;
449                 mutex_lock(&instance->bulk_waiter_list_mutex);
450                 list_add(&waiter->list, &instance->bulk_waiter_list);
451                 mutex_unlock(&instance->bulk_waiter_list_mutex);
452                 vchiq_log_info(vchiq_arm_log_level,
453                                 "saved bulk_waiter %x for pid %d",
454                                 (unsigned int)waiter, current->pid);
455         }
456
457         return status;
458 }