staging: vchiq: Move copy callback handling into vchiq
[linux-2.6-microblaze.git] / drivers / staging / vc04_services / interface / vchiq_arm / vchiq_shim.c
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright (c) 2010-2012 Broadcom. All rights reserved. */
3 #include <linux/module.h>
4 #include <linux/types.h>
5
6 #include "../vchi/vchi.h"
7 #include "vchiq.h"
8 #include "vchiq_core.h"
9
10 #include "vchiq_util.h"
11
12 #define vchiq_status_to_vchi(status) ((int32_t)status)
13
14 struct shim_service {
15         unsigned int handle;
16
17         struct vchiu_queue queue;
18
19         vchi_callback callback;
20         void *callback_param;
21 };
22
23 /***********************************************************
24  * Name: vchi_msg_peek
25  *
26  * Arguments:  struct vchi_service_handle *handle,
27  *             void **data,
28  *             uint32_t *msg_size,
29
30  *             enum vchi_flags flags
31  *
32  * Description: Routine to return a pointer to the current message (to allow in
33  *              place processing). The message can be removed using
34  *              vchi_msg_remove when you're finished
35  *
36  * Returns: int32_t - success == 0
37  *
38  ***********************************************************/
39 int32_t vchi_msg_peek(struct vchi_service_handle *handle,
40                       void **data,
41                       uint32_t *msg_size,
42                       enum vchi_flags flags)
43 {
44         struct shim_service *service = (struct shim_service *)handle;
45         struct vchiq_header *header;
46
47         WARN_ON((flags != VCHI_FLAGS_NONE) &&
48                 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
49
50         if (flags == VCHI_FLAGS_NONE)
51                 if (vchiu_queue_is_empty(&service->queue))
52                         return -1;
53
54         header = vchiu_queue_peek(&service->queue);
55
56         *data = header->data;
57         *msg_size = header->size;
58
59         return 0;
60 }
61 EXPORT_SYMBOL(vchi_msg_peek);
62
63 /***********************************************************
64  * Name: vchi_msg_remove
65  *
66  * Arguments:  struct vchi_service_handle *handle,
67  *
68  * Description: Routine to remove a message (after it has been read with
69  *              vchi_msg_peek)
70  *
71  * Returns: int32_t - success == 0
72  *
73  ***********************************************************/
74 int32_t vchi_msg_remove(struct vchi_service_handle *handle)
75 {
76         struct shim_service *service = (struct shim_service *)handle;
77         struct vchiq_header *header;
78
79         header = vchiu_queue_pop(&service->queue);
80
81         vchiq_release_message(service->handle, header);
82
83         return 0;
84 }
85 EXPORT_SYMBOL(vchi_msg_remove);
86
87 /***********************************************************
88  * Name: vchi_msg_queue
89  *
90  * Arguments:  struct vchi_service_handle *handle,
91  *             ssize_t (*copy_callback)(void *context, void *dest,
92  *                                      size_t offset, size_t maxsize),
93  *             void *context,
94  *             uint32_t data_size
95  *
96  * Description: Thin wrapper to queue a message onto a connection
97  *
98  * Returns: int32_t - success == 0
99  *
100  ***********************************************************/
101 static
102 int32_t vchi_msg_queue(struct vchi_service_handle *handle, void *context,
103                        uint32_t data_size)
104 {
105         struct shim_service *service = (struct shim_service *)handle;
106         enum vchiq_status status;
107
108         while (1) {
109                 status = vchiq_queue_kernel_message(service->handle, context,
110                                                     data_size);
111
112                 /*
113                  * vchiq_queue_message() may return VCHIQ_RETRY, so we need to
114                  * implement a retry mechanism since this function is supposed
115                  * to block until queued
116                  */
117                 if (status != VCHIQ_RETRY)
118                         break;
119
120                 msleep(1);
121         }
122
123         return vchiq_status_to_vchi(status);
124 }
125
126 int vchi_queue_kernel_message(struct vchi_service_handle *handle, void *data,
127                               unsigned int size)
128 {
129         return vchi_msg_queue(handle, data, size);
130 }
131 EXPORT_SYMBOL(vchi_queue_kernel_message);
132
133 /***********************************************************
134  * Name: vchi_bulk_queue_receive
135  *
136  * Arguments:  VCHI_BULK_HANDLE_T handle,
137  *             void *data_dst,
138  *             const uint32_t data_size,
139  *             enum vchi_flags flags
140  *             void *bulk_handle
141  *
142  * Description: Routine to setup a rcv buffer
143  *
144  * Returns: int32_t - success == 0
145  *
146  ***********************************************************/
147 int32_t vchi_bulk_queue_receive(struct vchi_service_handle *handle, void *data_dst,
148                                 uint32_t data_size, enum vchi_flags flags,
149                                 void *bulk_handle)
150 {
151         struct shim_service *service = (struct shim_service *)handle;
152         enum vchiq_bulk_mode mode;
153         enum vchiq_status status;
154
155         switch ((int)flags) {
156         case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE
157                 | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
158                 WARN_ON(!service->callback);
159                 mode = VCHIQ_BULK_MODE_CALLBACK;
160                 break;
161         case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
162                 mode = VCHIQ_BULK_MODE_BLOCKING;
163                 break;
164         case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
165         case VCHI_FLAGS_NONE:
166                 mode = VCHIQ_BULK_MODE_NOCALLBACK;
167                 break;
168         default:
169                 WARN(1, "unsupported message\n");
170                 return vchiq_status_to_vchi(VCHIQ_ERROR);
171         }
172
173         while (1) {
174                 status = vchiq_bulk_receive(service->handle, data_dst,
175                         data_size, bulk_handle, mode);
176                 /*
177                  * vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to
178                  * implement a retry mechanism since this function is supposed
179                  * to block until queued
180                  */
181                 if (status != VCHIQ_RETRY)
182                         break;
183
184                 msleep(1);
185         }
186
187         return vchiq_status_to_vchi(status);
188 }
189 EXPORT_SYMBOL(vchi_bulk_queue_receive);
190
191 /***********************************************************
192  * Name: vchi_bulk_queue_transmit
193  *
194  * Arguments:  VCHI_BULK_HANDLE_T handle,
195  *             const void *data_src,
196  *             uint32_t data_size,
197  *             enum vchi_flags flags,
198  *             void *bulk_handle
199  *
200  * Description: Routine to transmit some data
201  *
202  * Returns: int32_t - success == 0
203  *
204  ***********************************************************/
205 int32_t vchi_bulk_queue_transmit(struct vchi_service_handle *handle,
206                                  const void *data_src,
207                                  uint32_t data_size,
208                                  enum vchi_flags flags,
209                                  void *bulk_handle)
210 {
211         struct shim_service *service = (struct shim_service *)handle;
212         enum vchiq_bulk_mode mode;
213         enum vchiq_status status;
214
215         switch ((int)flags) {
216         case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE
217                 | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
218                 WARN_ON(!service->callback);
219                 mode = VCHIQ_BULK_MODE_CALLBACK;
220                 break;
221         case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
222         case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
223                 mode = VCHIQ_BULK_MODE_BLOCKING;
224                 break;
225         case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
226         case VCHI_FLAGS_NONE:
227                 mode = VCHIQ_BULK_MODE_NOCALLBACK;
228                 break;
229         default:
230                 WARN(1, "unsupported message\n");
231                 return vchiq_status_to_vchi(VCHIQ_ERROR);
232         }
233
234         while (1) {
235                 status = vchiq_bulk_transmit(service->handle, data_src,
236                         data_size, bulk_handle, mode);
237
238                 /*
239                  * vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to
240                  * implement a retry mechanism since this function is supposed
241                  * to block until queued
242                  */
243                 if (status != VCHIQ_RETRY)
244                         break;
245
246                 msleep(1);
247         }
248
249         return vchiq_status_to_vchi(status);
250 }
251 EXPORT_SYMBOL(vchi_bulk_queue_transmit);
252
253 /***********************************************************
254  * Name: vchi_msg_dequeue
255  *
256  * Arguments:  struct vchi_service_handle *handle,
257  *             void *data,
258  *             uint32_t max_data_size_to_read,
259  *             uint32_t *actual_msg_size
260  *             enum vchi_flags flags
261  *
262  * Description: Routine to dequeue a message into the supplied buffer
263  *
264  * Returns: int32_t - success == 0
265  *
266  ***********************************************************/
267 int32_t vchi_msg_dequeue(struct vchi_service_handle *handle, void *data,
268                          uint32_t max_data_size_to_read,
269                          uint32_t *actual_msg_size, enum vchi_flags flags)
270 {
271         struct shim_service *service = (struct shim_service *)handle;
272         struct vchiq_header *header;
273
274         WARN_ON((flags != VCHI_FLAGS_NONE) &&
275                 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
276
277         if (flags == VCHI_FLAGS_NONE)
278                 if (vchiu_queue_is_empty(&service->queue))
279                         return -1;
280
281         header = vchiu_queue_pop(&service->queue);
282
283         memcpy(data, header->data, header->size < max_data_size_to_read ?
284                 header->size : max_data_size_to_read);
285
286         *actual_msg_size = header->size;
287
288         vchiq_release_message(service->handle, header);
289
290         return 0;
291 }
292 EXPORT_SYMBOL(vchi_msg_dequeue);
293
294 /***********************************************************
295  * Name: vchi_held_msg_release
296  *
297  * Arguments:  struct vchi_held_msg *message
298  *
299  * Description: Routine to release a held message (after it has been read with
300  *              vchi_msg_hold)
301  *
302  * Returns: int32_t - success == 0
303  *
304  ***********************************************************/
305 int32_t vchi_held_msg_release(struct vchi_held_msg *message)
306 {
307         /*
308          * Convert the service field pointer back to an
309          * unsigned int which is an int.
310          * This pointer is opaque to everything except
311          * vchi_msg_hold which simply upcasted the int
312          * to a pointer.
313          */
314
315         vchiq_release_message((unsigned int)(long)message->service,
316                               (struct vchiq_header *)message->message);
317
318         return 0;
319 }
320 EXPORT_SYMBOL(vchi_held_msg_release);
321
322 /***********************************************************
323  * Name: vchi_msg_hold
324  *
325  * Arguments:  struct vchi_service_handle *handle,
326  *             void **data,
327  *             uint32_t *msg_size,
328  *             enum vchi_flags flags,
329  *             struct vchi_held_msg *message_handle
330  *
331  * Description: Routine to return a pointer to the current message (to allow
332  *              in place processing). The message is dequeued - don't forget
333  *              to release the message using vchi_held_msg_release when you're
334  *              finished.
335  *
336  * Returns: int32_t - success == 0
337  *
338  ***********************************************************/
339 int32_t vchi_msg_hold(struct vchi_service_handle *handle, void **data,
340                       uint32_t *msg_size, enum vchi_flags flags,
341                       struct vchi_held_msg *message_handle)
342 {
343         struct shim_service *service = (struct shim_service *)handle;
344         struct vchiq_header *header;
345
346         WARN_ON((flags != VCHI_FLAGS_NONE) &&
347                 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
348
349         if (flags == VCHI_FLAGS_NONE)
350                 if (vchiu_queue_is_empty(&service->queue))
351                         return -1;
352
353         header = vchiu_queue_pop(&service->queue);
354
355         *data = header->data;
356         *msg_size = header->size;
357
358         /*
359          * upcast the unsigned int which is an int
360          * to a pointer and stuff it in the held message.
361          * This pointer is opaque to everything except
362          * vchi_held_msg_release which simply downcasts it back
363          * to an int.
364          */
365
366         message_handle->service =
367                 (struct opaque_vchi_service_t *)(long)service->handle;
368         message_handle->message = header;
369
370         return 0;
371 }
372 EXPORT_SYMBOL(vchi_msg_hold);
373
374 /***********************************************************
375  * Name: vchi_initialise
376  *
377  * Arguments: struct vchi_instance_handle **instance_handle
378  *
379  * Description: Initialises the hardware but does not transmit anything
380  *              When run as a Host App this will be called twice hence the need
381  *              to malloc the state information
382  *
383  * Returns: 0 if successful, failure otherwise
384  *
385  ***********************************************************/
386
387 int32_t vchi_initialise(struct vchi_instance_handle **instance_handle)
388 {
389         struct vchiq_instance *instance;
390         enum vchiq_status status;
391
392         status = vchiq_initialise(&instance);
393
394         *instance_handle = (struct vchi_instance_handle *)instance;
395
396         return vchiq_status_to_vchi(status);
397 }
398 EXPORT_SYMBOL(vchi_initialise);
399
400 /***********************************************************
401  * Name: vchi_connect
402  *
403  * Arguments: struct vchi_instance_handle *instance_handle
404  *
405  * Description: Starts the command service on each connection,
406  *              causing INIT messages to be pinged back and forth
407  *
408  * Returns: 0 if successful, failure otherwise
409  *
410  ***********************************************************/
411 int32_t vchi_connect(struct vchi_instance_handle *instance_handle)
412 {
413         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
414
415         return vchiq_connect(instance);
416 }
417 EXPORT_SYMBOL(vchi_connect);
418
419 /***********************************************************
420  * Name: vchi_disconnect
421  *
422  * Arguments: struct vchi_instance_handle *instance_handle
423  *
424  * Description: Stops the command service on each connection,
425  *              causing DE-INIT messages to be pinged back and forth
426  *
427  * Returns: 0 if successful, failure otherwise
428  *
429  ***********************************************************/
430 int32_t vchi_disconnect(struct vchi_instance_handle *instance_handle)
431 {
432         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
433
434         return vchiq_status_to_vchi(vchiq_shutdown(instance));
435 }
436 EXPORT_SYMBOL(vchi_disconnect);
437
438 /***********************************************************
439  * Name: vchi_service_open
440  * Name: vchi_service_create
441  *
442  * Arguments: struct vchi_instance_handle *instance_handle
443  *            struct service_creation *setup,
444  *            struct vchi_service_handle **handle
445  *
446  * Description: Routine to open a service
447  *
448  * Returns: int32_t - success == 0
449  *
450  ***********************************************************/
451
452 static enum vchiq_status shim_callback(enum vchiq_reason reason,
453                                     struct vchiq_header *header,
454                                     unsigned int handle,
455                                     void *bulk_user)
456 {
457         struct shim_service *service =
458                 (struct shim_service *)VCHIQ_GET_SERVICE_USERDATA(handle);
459
460         if (!service->callback)
461                 goto release;
462
463         switch (reason) {
464         case VCHIQ_MESSAGE_AVAILABLE:
465                 vchiu_queue_push(&service->queue, header);
466
467                 service->callback(service->callback_param,
468                                   VCHI_CALLBACK_MSG_AVAILABLE, NULL);
469
470                 goto done;
471
472         case VCHIQ_BULK_TRANSMIT_DONE:
473                 service->callback(service->callback_param,
474                                   VCHI_CALLBACK_BULK_SENT, bulk_user);
475                 break;
476
477         case VCHIQ_BULK_RECEIVE_DONE:
478                 service->callback(service->callback_param,
479                                   VCHI_CALLBACK_BULK_RECEIVED, bulk_user);
480                 break;
481
482         case VCHIQ_SERVICE_CLOSED:
483                 service->callback(service->callback_param,
484                                   VCHI_CALLBACK_SERVICE_CLOSED, NULL);
485                 break;
486
487         case VCHIQ_SERVICE_OPENED:
488                 /* No equivalent VCHI reason */
489                 break;
490
491         case VCHIQ_BULK_TRANSMIT_ABORTED:
492                 service->callback(service->callback_param,
493                                   VCHI_CALLBACK_BULK_TRANSMIT_ABORTED,
494                                   bulk_user);
495                 break;
496
497         case VCHIQ_BULK_RECEIVE_ABORTED:
498                 service->callback(service->callback_param,
499                                   VCHI_CALLBACK_BULK_RECEIVE_ABORTED,
500                                   bulk_user);
501                 break;
502
503         default:
504                 WARN(1, "not supported\n");
505                 break;
506         }
507
508 release:
509         vchiq_release_message(service->handle, header);
510 done:
511         return VCHIQ_SUCCESS;
512 }
513
514 static struct shim_service *service_alloc(struct vchiq_instance *instance,
515         struct service_creation *setup)
516 {
517         struct shim_service *service = kzalloc(sizeof(struct shim_service), GFP_KERNEL);
518
519         (void)instance;
520
521         if (service) {
522                 if (!vchiu_queue_init(&service->queue, 64)) {
523                         service->callback = setup->callback;
524                         service->callback_param = setup->callback_param;
525                 } else {
526                         kfree(service);
527                         service = NULL;
528                 }
529         }
530
531         return service;
532 }
533
534 static void service_free(struct shim_service *service)
535 {
536         if (service) {
537                 vchiu_queue_delete(&service->queue);
538                 kfree(service);
539         }
540 }
541
542 int32_t vchi_service_open(struct vchi_instance_handle *instance_handle,
543         struct service_creation *setup,
544         struct vchi_service_handle **handle)
545 {
546         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
547         struct shim_service *service = service_alloc(instance, setup);
548
549         *handle = (struct vchi_service_handle *)service;
550
551         if (service) {
552                 struct vchiq_service_params params;
553                 enum vchiq_status status;
554
555                 memset(&params, 0, sizeof(params));
556                 params.fourcc = setup->service_id;
557                 params.callback = shim_callback;
558                 params.userdata = service;
559                 params.version = setup->version.version;
560                 params.version_min = setup->version.version_min;
561
562                 status = vchiq_open_service(instance, &params,
563                         &service->handle);
564                 if (status != VCHIQ_SUCCESS) {
565                         service_free(service);
566                         service = NULL;
567                         *handle = NULL;
568                 }
569         }
570
571         return service ? 0 : -1;
572 }
573 EXPORT_SYMBOL(vchi_service_open);
574
575 int32_t vchi_service_close(const struct vchi_service_handle *handle)
576 {
577         int32_t ret = -1;
578         struct shim_service *service = (struct shim_service *)handle;
579
580         if (service) {
581                 enum vchiq_status status = vchiq_close_service(service->handle);
582                 if (status == VCHIQ_SUCCESS)
583                         service_free(service);
584
585                 ret = vchiq_status_to_vchi(status);
586         }
587         return ret;
588 }
589 EXPORT_SYMBOL(vchi_service_close);
590
591 int32_t vchi_service_set_option(const struct vchi_service_handle *handle,
592                                 enum vchi_service_option option,
593                                 int value)
594 {
595         int32_t ret = -1;
596         struct shim_service *service = (struct shim_service *)handle;
597         enum vchiq_service_option vchiq_option;
598
599         switch (option) {
600         case VCHI_SERVICE_OPTION_TRACE:
601                 vchiq_option = VCHIQ_SERVICE_OPTION_TRACE;
602                 break;
603         case VCHI_SERVICE_OPTION_SYNCHRONOUS:
604                 vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS;
605                 break;
606         default:
607                 service = NULL;
608                 break;
609         }
610         if (service) {
611                 enum vchiq_status status =
612                         vchiq_set_service_option(service->handle,
613                                                 vchiq_option,
614                                                 value);
615
616                 ret = vchiq_status_to_vchi(status);
617         }
618         return ret;
619 }
620 EXPORT_SYMBOL(vchi_service_set_option);
621
622 int32_t vchi_get_peer_version(const struct vchi_service_handle *handle, short *peer_version)
623 {
624         int32_t ret = -1;
625         struct shim_service *service = (struct shim_service *)handle;
626
627         if (service) {
628                 enum vchiq_status status;
629
630                 status = vchiq_get_peer_version(service->handle, peer_version);
631                 ret = vchiq_status_to_vchi(status);
632         }
633         return ret;
634 }
635 EXPORT_SYMBOL(vchi_get_peer_version);
636
637 /***********************************************************
638  * Name: vchi_service_use
639  *
640  * Arguments: const struct vchi_service_handle *handle
641  *
642  * Description: Routine to increment refcount on a service
643  *
644  * Returns: void
645  *
646  ***********************************************************/
647 int32_t vchi_service_use(const struct vchi_service_handle *handle)
648 {
649         int32_t ret = -1;
650
651         struct shim_service *service = (struct shim_service *)handle;
652         if (service)
653                 ret = vchiq_status_to_vchi(vchiq_use_service(service->handle));
654         return ret;
655 }
656 EXPORT_SYMBOL(vchi_service_use);
657
658 /***********************************************************
659  * Name: vchi_service_release
660  *
661  * Arguments: const struct vchi_service_handle *handle
662  *
663  * Description: Routine to decrement refcount on a service
664  *
665  * Returns: void
666  *
667  ***********************************************************/
668 int32_t vchi_service_release(const struct vchi_service_handle *handle)
669 {
670         int32_t ret = -1;
671
672         struct shim_service *service = (struct shim_service *)handle;
673         if (service)
674                 ret = vchiq_status_to_vchi(
675                         vchiq_release_service(service->handle));
676         return ret;
677 }
678 EXPORT_SYMBOL(vchi_service_release);