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