GNU Linux-libre 4.14.290-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 *instance_out)
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         *instance_out = 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_killable(&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
151                 list_for_each_safe(pos, next,
152                                 &instance->bulk_waiter_list) {
153                         struct bulk_waiter_node *waiter;
154
155                         waiter = list_entry(pos,
156                                         struct bulk_waiter_node,
157                                         list);
158                         list_del(pos);
159                         vchiq_log_info(vchiq_arm_log_level,
160                                         "bulk_waiter - cleaned up %pK for pid %d",
161                                         waiter, waiter->pid);
162                         kfree(waiter);
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
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
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 (mutex_lock_killable(&state->mutex) != 0) {
197                 vchiq_log_trace(vchiq_core_log_level,
198                         "%s: call to mutex_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         mutex_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
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
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, current->pid);
294                 if (status != VCHIQ_SUCCESS) {
295                         vchiq_remove_service(service->handle);
296                         *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
297                 }
298         }
299
300 failed:
301         vchiq_log_trace(vchiq_core_log_level,
302                 "%s(%p): returning %d", __func__, instance, status);
303
304         return status;
305 }
306 EXPORT_SYMBOL(vchiq_open_service);
307
308 VCHIQ_STATUS_T
309 vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
310         const void *data, unsigned int size, void *userdata)
311 {
312         return vchiq_bulk_transfer(handle,
313                 VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata,
314                 VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT);
315 }
316 EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
317
318 VCHIQ_STATUS_T
319 vchiq_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 }
326 EXPORT_SYMBOL(vchiq_queue_bulk_receive);
327
328 VCHIQ_STATUS_T
329 vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const 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, (void *)data, size, userdata,
339                         mode, VCHIQ_BULK_TRANSMIT);
340                 break;
341         case VCHIQ_BULK_MODE_BLOCKING:
342                 status = vchiq_blocking_bulk_transfer(handle,
343                         (void *)data, size, VCHIQ_BULK_TRANSMIT);
344                 break;
345         default:
346                 return VCHIQ_ERROR;
347         }
348
349         return status;
350 }
351 EXPORT_SYMBOL(vchiq_bulk_transmit);
352
353 VCHIQ_STATUS_T
354 vchiq_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                         (void *)data, size, VCHIQ_BULK_RECEIVE);
369                 break;
370         default:
371                 return VCHIQ_ERROR;
372         }
373
374         return status;
375 }
376 EXPORT_SYMBOL(vchiq_bulk_receive);
377
378 static VCHIQ_STATUS_T
379 vchiq_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         mutex_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->pid) {
400                         waiter = list_entry(pos,
401                                 struct bulk_waiter_node,
402                                 list);
403                         list_del(pos);
404                         break;
405                 }
406         }
407         mutex_unlock(&instance->bulk_waiter_list_mutex);
408
409         if (waiter) {
410                 VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk;
411
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
442                 if (bulk) {
443                         /* Cancel the signal when the transfer
444                          ** completes. */
445                         spin_lock(&bulk_waiter_spinlock);
446                         bulk->userdata = NULL;
447                         spin_unlock(&bulk_waiter_spinlock);
448                 }
449                 kfree(waiter);
450         } else {
451                 waiter->pid = current->pid;
452                 mutex_lock(&instance->bulk_waiter_list_mutex);
453                 list_add(&waiter->list, &instance->bulk_waiter_list);
454                 mutex_unlock(&instance->bulk_waiter_list_mutex);
455                 vchiq_log_info(vchiq_arm_log_level,
456                                 "saved bulk_waiter %pK for pid %d",
457                                 waiter, current->pid);
458         }
459
460         return status;
461 }