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 
43 struct bulk_waiter_node {
44           struct bulk_waiter bulk_waiter;
45           int pid;
46           struct list_head list;
47 };
48 
49 struct 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 
58 static VCHIQ_STATUS_T
59 vchiq_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
vchiq_initialise(VCHIQ_INSTANCE_T * instanceOut)68 VCHIQ_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", __func__);
88                     goto failed;
89           } else if (i>0) {
90                     vchiq_log_warning(vchiq_core_log_level,
91                               "%s: videocore initialized after %d retries", __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", __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 
110 failed:
111           vchiq_log_trace(vchiq_core_log_level,
112                     "%s(%p): returning %d", __func__, instance, status);
113 
114           return status;
115 }
116 EXPORT_SYMBOL(vchiq_initialise);
117 
118 /****************************************************************************
119 *
120 *   vchiq_shutdown
121 *
122 ***************************************************************************/
123 
vchiq_shutdown(VCHIQ_INSTANCE_T instance)124 VCHIQ_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 %p "
154                                                   "for pid %d",
155                                                   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 }
168 EXPORT_SYMBOL(vchiq_shutdown);
169 
170 /****************************************************************************
171 *
172 *   vchiq_is_connected
173 *
174 ***************************************************************************/
175 
vchiq_is_connected(VCHIQ_INSTANCE_T instance)176 static int vchiq_is_connected(VCHIQ_INSTANCE_T instance)
177 {
178           return instance->connected;
179 }
180 
181 /****************************************************************************
182 *
183 *   vchiq_connect
184 *
185 ***************************************************************************/
186 
vchiq_connect(VCHIQ_INSTANCE_T instance)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 (lmutex_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           lmutex_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 
vchiq_add_service(VCHIQ_INSTANCE_T instance,const VCHIQ_SERVICE_PARAMS_T * params,VCHIQ_SERVICE_HANDLE_T * phandle)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 
vchiq_open_service(VCHIQ_INSTANCE_T instance,const VCHIQ_SERVICE_PARAMS_T * params,VCHIQ_SERVICE_HANDLE_T * phandle)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,
293                         current->l_proc->p_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
vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata)309 vchiq_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 }
316 EXPORT_SYMBOL(vchiq_queue_bulk_transmit);
317 
318 VCHIQ_STATUS_T
vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata)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
vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata,VCHIQ_BULK_MODE_T mode)329 vchiq_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 }
351 EXPORT_SYMBOL(vchiq_bulk_transmit);
352 
353 VCHIQ_STATUS_T
vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,void * userdata,VCHIQ_BULK_MODE_T mode)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                               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
vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle,void * data,unsigned int size,VCHIQ_BULK_DIR_T dir)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           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->l_proc->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->l_proc->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 %p for pid %d",
457                                         waiter, current->l_proc->p_pid);
458           }
459 
460           return status;
461 }
462