Merge tag 'ntb-5.5' of git://github.com/jonmason/ntb
[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 "interface/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,
103         ssize_t (*copy_callback)(void *context, void *dest,
104                                  size_t offset, size_t maxsize),
105         void *context,
106         uint32_t data_size)
107 {
108         struct shim_service *service = (struct shim_service *)handle;
109         enum vchiq_status status;
110
111         while (1) {
112                 status = vchiq_queue_message(service->handle,
113                                              copy_callback,
114                                              context,
115                                              data_size);
116
117                 /*
118                  * vchiq_queue_message() may return VCHIQ_RETRY, so we need to
119                  * implement a retry mechanism since this function is supposed
120                  * to block until queued
121                  */
122                 if (status != VCHIQ_RETRY)
123                         break;
124
125                 msleep(1);
126         }
127
128         return vchiq_status_to_vchi(status);
129 }
130
131 static ssize_t
132 vchi_queue_kernel_message_callback(void *context,
133                                    void *dest,
134                                    size_t offset,
135                                    size_t maxsize)
136 {
137         memcpy(dest, context + offset, maxsize);
138         return maxsize;
139 }
140
141 int
142 vchi_queue_kernel_message(struct vchi_service_handle *handle,
143                           void *data,
144                           unsigned int size)
145 {
146         return vchi_msg_queue(handle,
147                               vchi_queue_kernel_message_callback,
148                               data,
149                               size);
150 }
151 EXPORT_SYMBOL(vchi_queue_kernel_message);
152
153 struct vchi_queue_user_message_context {
154         void __user *data;
155 };
156
157 static ssize_t
158 vchi_queue_user_message_callback(void *context,
159                                  void *dest,
160                                  size_t offset,
161                                  size_t maxsize)
162 {
163         struct vchi_queue_user_message_context *copycontext = context;
164
165         if (copy_from_user(dest, copycontext->data + offset, maxsize))
166                 return -EFAULT;
167
168         return maxsize;
169 }
170
171 int
172 vchi_queue_user_message(struct vchi_service_handle *handle,
173                         void __user *data,
174                         unsigned int size)
175 {
176         struct vchi_queue_user_message_context copycontext = {
177                 .data = data
178         };
179
180         return vchi_msg_queue(handle,
181                               vchi_queue_user_message_callback,
182                               &copycontext,
183                               size);
184 }
185 EXPORT_SYMBOL(vchi_queue_user_message);
186
187 /***********************************************************
188  * Name: vchi_bulk_queue_receive
189  *
190  * Arguments:  VCHI_BULK_HANDLE_T handle,
191  *             void *data_dst,
192  *             const uint32_t data_size,
193  *             enum vchi_flags flags
194  *             void *bulk_handle
195  *
196  * Description: Routine to setup a rcv buffer
197  *
198  * Returns: int32_t - success == 0
199  *
200  ***********************************************************/
201 int32_t vchi_bulk_queue_receive(struct vchi_service_handle *handle, void *data_dst,
202                                 uint32_t data_size, enum vchi_flags flags,
203                                 void *bulk_handle)
204 {
205         struct shim_service *service = (struct shim_service *)handle;
206         enum vchiq_bulk_mode mode;
207         enum vchiq_status status;
208
209         switch ((int)flags) {
210         case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE
211                 | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
212                 WARN_ON(!service->callback);
213                 mode = VCHIQ_BULK_MODE_CALLBACK;
214                 break;
215         case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
216                 mode = VCHIQ_BULK_MODE_BLOCKING;
217                 break;
218         case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
219         case VCHI_FLAGS_NONE:
220                 mode = VCHIQ_BULK_MODE_NOCALLBACK;
221                 break;
222         default:
223                 WARN(1, "unsupported message\n");
224                 return vchiq_status_to_vchi(VCHIQ_ERROR);
225         }
226
227         while (1) {
228                 status = vchiq_bulk_receive(service->handle, data_dst,
229                         data_size, bulk_handle, mode);
230                 /*
231                  * vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to
232                  * implement a retry mechanism since this function is supposed
233                  * to block until queued
234                  */
235                 if (status != VCHIQ_RETRY)
236                         break;
237
238                 msleep(1);
239         }
240
241         return vchiq_status_to_vchi(status);
242 }
243 EXPORT_SYMBOL(vchi_bulk_queue_receive);
244
245 /***********************************************************
246  * Name: vchi_bulk_queue_transmit
247  *
248  * Arguments:  VCHI_BULK_HANDLE_T handle,
249  *             const void *data_src,
250  *             uint32_t data_size,
251  *             enum vchi_flags flags,
252  *             void *bulk_handle
253  *
254  * Description: Routine to transmit some data
255  *
256  * Returns: int32_t - success == 0
257  *
258  ***********************************************************/
259 int32_t vchi_bulk_queue_transmit(struct vchi_service_handle *handle,
260                                  const void *data_src,
261                                  uint32_t data_size,
262                                  enum vchi_flags flags,
263                                  void *bulk_handle)
264 {
265         struct shim_service *service = (struct shim_service *)handle;
266         enum vchiq_bulk_mode mode;
267         enum vchiq_status status;
268
269         switch ((int)flags) {
270         case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE
271                 | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
272                 WARN_ON(!service->callback);
273                 mode = VCHIQ_BULK_MODE_CALLBACK;
274                 break;
275         case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
276         case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
277                 mode = VCHIQ_BULK_MODE_BLOCKING;
278                 break;
279         case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
280         case VCHI_FLAGS_NONE:
281                 mode = VCHIQ_BULK_MODE_NOCALLBACK;
282                 break;
283         default:
284                 WARN(1, "unsupported message\n");
285                 return vchiq_status_to_vchi(VCHIQ_ERROR);
286         }
287
288         while (1) {
289                 status = vchiq_bulk_transmit(service->handle, data_src,
290                         data_size, bulk_handle, mode);
291
292                 /*
293                  * vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to
294                  * implement a retry mechanism since this function is supposed
295                  * to block until queued
296                  */
297                 if (status != VCHIQ_RETRY)
298                         break;
299
300                 msleep(1);
301         }
302
303         return vchiq_status_to_vchi(status);
304 }
305 EXPORT_SYMBOL(vchi_bulk_queue_transmit);
306
307 /***********************************************************
308  * Name: vchi_msg_dequeue
309  *
310  * Arguments:  struct vchi_service_handle *handle,
311  *             void *data,
312  *             uint32_t max_data_size_to_read,
313  *             uint32_t *actual_msg_size
314  *             enum vchi_flags flags
315  *
316  * Description: Routine to dequeue a message into the supplied buffer
317  *
318  * Returns: int32_t - success == 0
319  *
320  ***********************************************************/
321 int32_t vchi_msg_dequeue(struct vchi_service_handle *handle, void *data,
322                          uint32_t max_data_size_to_read,
323                          uint32_t *actual_msg_size, enum vchi_flags flags)
324 {
325         struct shim_service *service = (struct shim_service *)handle;
326         struct vchiq_header *header;
327
328         WARN_ON((flags != VCHI_FLAGS_NONE) &&
329                 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
330
331         if (flags == VCHI_FLAGS_NONE)
332                 if (vchiu_queue_is_empty(&service->queue))
333                         return -1;
334
335         header = vchiu_queue_pop(&service->queue);
336
337         memcpy(data, header->data, header->size < max_data_size_to_read ?
338                 header->size : max_data_size_to_read);
339
340         *actual_msg_size = header->size;
341
342         vchiq_release_message(service->handle, header);
343
344         return 0;
345 }
346 EXPORT_SYMBOL(vchi_msg_dequeue);
347
348 /***********************************************************
349  * Name: vchi_held_msg_release
350  *
351  * Arguments:  struct vchi_held_msg *message
352  *
353  * Description: Routine to release a held message (after it has been read with
354  *              vchi_msg_hold)
355  *
356  * Returns: int32_t - success == 0
357  *
358  ***********************************************************/
359 int32_t vchi_held_msg_release(struct vchi_held_msg *message)
360 {
361         /*
362          * Convert the service field pointer back to an
363          * unsigned int which is an int.
364          * This pointer is opaque to everything except
365          * vchi_msg_hold which simply upcasted the int
366          * to a pointer.
367          */
368
369         vchiq_release_message((unsigned int)(long)message->service,
370                               (struct vchiq_header *)message->message);
371
372         return 0;
373 }
374 EXPORT_SYMBOL(vchi_held_msg_release);
375
376 /***********************************************************
377  * Name: vchi_msg_hold
378  *
379  * Arguments:  struct vchi_service_handle *handle,
380  *             void **data,
381  *             uint32_t *msg_size,
382  *             enum vchi_flags flags,
383  *             struct vchi_held_msg *message_handle
384  *
385  * Description: Routine to return a pointer to the current message (to allow
386  *              in place processing). The message is dequeued - don't forget
387  *              to release the message using vchi_held_msg_release when you're
388  *              finished.
389  *
390  * Returns: int32_t - success == 0
391  *
392  ***********************************************************/
393 int32_t vchi_msg_hold(struct vchi_service_handle *handle, void **data,
394                       uint32_t *msg_size, enum vchi_flags flags,
395                       struct vchi_held_msg *message_handle)
396 {
397         struct shim_service *service = (struct shim_service *)handle;
398         struct vchiq_header *header;
399
400         WARN_ON((flags != VCHI_FLAGS_NONE) &&
401                 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
402
403         if (flags == VCHI_FLAGS_NONE)
404                 if (vchiu_queue_is_empty(&service->queue))
405                         return -1;
406
407         header = vchiu_queue_pop(&service->queue);
408
409         *data = header->data;
410         *msg_size = header->size;
411
412         /*
413          * upcast the unsigned int which is an int
414          * to a pointer and stuff it in the held message.
415          * This pointer is opaque to everything except
416          * vchi_held_msg_release which simply downcasts it back
417          * to an int.
418          */
419
420         message_handle->service =
421                 (struct opaque_vchi_service_t *)(long)service->handle;
422         message_handle->message = header;
423
424         return 0;
425 }
426 EXPORT_SYMBOL(vchi_msg_hold);
427
428 /***********************************************************
429  * Name: vchi_initialise
430  *
431  * Arguments: struct vchi_instance_handle **instance_handle
432  *
433  * Description: Initialises the hardware but does not transmit anything
434  *              When run as a Host App this will be called twice hence the need
435  *              to malloc the state information
436  *
437  * Returns: 0 if successful, failure otherwise
438  *
439  ***********************************************************/
440
441 int32_t vchi_initialise(struct vchi_instance_handle **instance_handle)
442 {
443         struct vchiq_instance *instance;
444         enum vchiq_status status;
445
446         status = vchiq_initialise(&instance);
447
448         *instance_handle = (struct vchi_instance_handle *)instance;
449
450         return vchiq_status_to_vchi(status);
451 }
452 EXPORT_SYMBOL(vchi_initialise);
453
454 /***********************************************************
455  * Name: vchi_connect
456  *
457  * Arguments: struct vchi_instance_handle *instance_handle
458  *
459  * Description: Starts the command service on each connection,
460  *              causing INIT messages to be pinged back and forth
461  *
462  * Returns: 0 if successful, failure otherwise
463  *
464  ***********************************************************/
465 int32_t vchi_connect(struct vchi_instance_handle *instance_handle)
466 {
467         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
468
469         return vchiq_connect(instance);
470 }
471 EXPORT_SYMBOL(vchi_connect);
472
473 /***********************************************************
474  * Name: vchi_disconnect
475  *
476  * Arguments: struct vchi_instance_handle *instance_handle
477  *
478  * Description: Stops the command service on each connection,
479  *              causing DE-INIT messages to be pinged back and forth
480  *
481  * Returns: 0 if successful, failure otherwise
482  *
483  ***********************************************************/
484 int32_t vchi_disconnect(struct vchi_instance_handle *instance_handle)
485 {
486         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
487
488         return vchiq_status_to_vchi(vchiq_shutdown(instance));
489 }
490 EXPORT_SYMBOL(vchi_disconnect);
491
492 /***********************************************************
493  * Name: vchi_service_open
494  * Name: vchi_service_create
495  *
496  * Arguments: struct vchi_instance_handle *instance_handle
497  *            struct service_creation *setup,
498  *            struct vchi_service_handle **handle
499  *
500  * Description: Routine to open a service
501  *
502  * Returns: int32_t - success == 0
503  *
504  ***********************************************************/
505
506 static enum vchiq_status shim_callback(enum vchiq_reason reason,
507                                     struct vchiq_header *header,
508                                     unsigned int handle,
509                                     void *bulk_user)
510 {
511         struct shim_service *service =
512                 (struct shim_service *)VCHIQ_GET_SERVICE_USERDATA(handle);
513
514         if (!service->callback)
515                 goto release;
516
517         switch (reason) {
518         case VCHIQ_MESSAGE_AVAILABLE:
519                 vchiu_queue_push(&service->queue, header);
520
521                 service->callback(service->callback_param,
522                                   VCHI_CALLBACK_MSG_AVAILABLE, NULL);
523
524                 goto done;
525
526         case VCHIQ_BULK_TRANSMIT_DONE:
527                 service->callback(service->callback_param,
528                                   VCHI_CALLBACK_BULK_SENT, bulk_user);
529                 break;
530
531         case VCHIQ_BULK_RECEIVE_DONE:
532                 service->callback(service->callback_param,
533                                   VCHI_CALLBACK_BULK_RECEIVED, bulk_user);
534                 break;
535
536         case VCHIQ_SERVICE_CLOSED:
537                 service->callback(service->callback_param,
538                                   VCHI_CALLBACK_SERVICE_CLOSED, NULL);
539                 break;
540
541         case VCHIQ_SERVICE_OPENED:
542                 /* No equivalent VCHI reason */
543                 break;
544
545         case VCHIQ_BULK_TRANSMIT_ABORTED:
546                 service->callback(service->callback_param,
547                                   VCHI_CALLBACK_BULK_TRANSMIT_ABORTED,
548                                   bulk_user);
549                 break;
550
551         case VCHIQ_BULK_RECEIVE_ABORTED:
552                 service->callback(service->callback_param,
553                                   VCHI_CALLBACK_BULK_RECEIVE_ABORTED,
554                                   bulk_user);
555                 break;
556
557         default:
558                 WARN(1, "not supported\n");
559                 break;
560         }
561
562 release:
563         vchiq_release_message(service->handle, header);
564 done:
565         return VCHIQ_SUCCESS;
566 }
567
568 static struct shim_service *service_alloc(struct vchiq_instance *instance,
569         struct service_creation *setup)
570 {
571         struct shim_service *service = kzalloc(sizeof(struct shim_service), GFP_KERNEL);
572
573         (void)instance;
574
575         if (service) {
576                 if (!vchiu_queue_init(&service->queue, 64)) {
577                         service->callback = setup->callback;
578                         service->callback_param = setup->callback_param;
579                 } else {
580                         kfree(service);
581                         service = NULL;
582                 }
583         }
584
585         return service;
586 }
587
588 static void service_free(struct shim_service *service)
589 {
590         if (service) {
591                 vchiu_queue_delete(&service->queue);
592                 kfree(service);
593         }
594 }
595
596 int32_t vchi_service_open(struct vchi_instance_handle *instance_handle,
597         struct service_creation *setup,
598         struct vchi_service_handle **handle)
599 {
600         struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
601         struct shim_service *service = service_alloc(instance, setup);
602
603         *handle = (struct vchi_service_handle *)service;
604
605         if (service) {
606                 struct vchiq_service_params params;
607                 enum vchiq_status status;
608
609                 memset(&params, 0, sizeof(params));
610                 params.fourcc = setup->service_id;
611                 params.callback = shim_callback;
612                 params.userdata = service;
613                 params.version = setup->version.version;
614                 params.version_min = setup->version.version_min;
615
616                 status = vchiq_open_service(instance, &params,
617                         &service->handle);
618                 if (status != VCHIQ_SUCCESS) {
619                         service_free(service);
620                         service = NULL;
621                         *handle = NULL;
622                 }
623         }
624
625         return service ? 0 : -1;
626 }
627 EXPORT_SYMBOL(vchi_service_open);
628
629 int32_t vchi_service_close(const struct vchi_service_handle *handle)
630 {
631         int32_t ret = -1;
632         struct shim_service *service = (struct shim_service *)handle;
633
634         if (service) {
635                 enum vchiq_status status = vchiq_close_service(service->handle);
636                 if (status == VCHIQ_SUCCESS)
637                         service_free(service);
638
639                 ret = vchiq_status_to_vchi(status);
640         }
641         return ret;
642 }
643 EXPORT_SYMBOL(vchi_service_close);
644
645 int32_t vchi_service_destroy(const struct vchi_service_handle *handle)
646 {
647         int32_t ret = -1;
648         struct shim_service *service = (struct shim_service *)handle;
649
650         if (service) {
651                 enum vchiq_status status = vchiq_remove_service(service->handle);
652
653                 if (status == VCHIQ_SUCCESS) {
654                         service_free(service);
655                         service = NULL;
656                 }
657
658                 ret = vchiq_status_to_vchi(status);
659         }
660         return ret;
661 }
662 EXPORT_SYMBOL(vchi_service_destroy);
663
664 int32_t vchi_service_set_option(const struct vchi_service_handle *handle,
665                                 enum vchi_service_option option,
666                                 int value)
667 {
668         int32_t ret = -1;
669         struct shim_service *service = (struct shim_service *)handle;
670         enum vchiq_service_option vchiq_option;
671
672         switch (option) {
673         case VCHI_SERVICE_OPTION_TRACE:
674                 vchiq_option = VCHIQ_SERVICE_OPTION_TRACE;
675                 break;
676         case VCHI_SERVICE_OPTION_SYNCHRONOUS:
677                 vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS;
678                 break;
679         default:
680                 service = NULL;
681                 break;
682         }
683         if (service) {
684                 enum vchiq_status status =
685                         vchiq_set_service_option(service->handle,
686                                                 vchiq_option,
687                                                 value);
688
689                 ret = vchiq_status_to_vchi(status);
690         }
691         return ret;
692 }
693 EXPORT_SYMBOL(vchi_service_set_option);
694
695 int32_t vchi_get_peer_version(const struct vchi_service_handle *handle, short *peer_version)
696 {
697         int32_t ret = -1;
698         struct shim_service *service = (struct shim_service *)handle;
699
700         if (service) {
701                 enum vchiq_status status;
702
703                 status = vchiq_get_peer_version(service->handle, peer_version);
704                 ret = vchiq_status_to_vchi(status);
705         }
706         return ret;
707 }
708 EXPORT_SYMBOL(vchi_get_peer_version);
709
710 /***********************************************************
711  * Name: vchi_service_use
712  *
713  * Arguments: const struct vchi_service_handle *handle
714  *
715  * Description: Routine to increment refcount on a service
716  *
717  * Returns: void
718  *
719  ***********************************************************/
720 int32_t vchi_service_use(const struct vchi_service_handle *handle)
721 {
722         int32_t ret = -1;
723
724         struct shim_service *service = (struct shim_service *)handle;
725         if (service)
726                 ret = vchiq_status_to_vchi(vchiq_use_service(service->handle));
727         return ret;
728 }
729 EXPORT_SYMBOL(vchi_service_use);
730
731 /***********************************************************
732  * Name: vchi_service_release
733  *
734  * Arguments: const struct vchi_service_handle *handle
735  *
736  * Description: Routine to decrement refcount on a service
737  *
738  * Returns: void
739  *
740  ***********************************************************/
741 int32_t vchi_service_release(const struct vchi_service_handle *handle)
742 {
743         int32_t ret = -1;
744
745         struct shim_service *service = (struct shim_service *)handle;
746         if (service)
747                 ret = vchiq_status_to_vchi(
748                         vchiq_release_service(service->handle));
749         return ret;
750 }
751 EXPORT_SYMBOL(vchi_service_release);