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>
6 #include "../vchi/vchi.h"
8 #include "vchiq_core.h"
10 #include "vchiq_util.h"
12 #define vchiq_status_to_vchi(status) ((int32_t)status)
17 struct vchiu_queue queue;
19 vchi_callback callback;
23 /***********************************************************
26 * Arguments: struct vchi_service_handle *handle,
30 * enum vchi_flags flags
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
36 * Returns: int32_t - success == 0
38 ***********************************************************/
39 int32_t vchi_msg_peek(struct vchi_service_handle *handle,
42 enum vchi_flags flags)
44 struct shim_service *service = (struct shim_service *)handle;
45 struct vchiq_header *header;
47 WARN_ON((flags != VCHI_FLAGS_NONE) &&
48 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
50 if (flags == VCHI_FLAGS_NONE)
51 if (vchiu_queue_is_empty(&service->queue))
54 header = vchiu_queue_peek(&service->queue);
57 *msg_size = header->size;
61 EXPORT_SYMBOL(vchi_msg_peek);
63 /***********************************************************
64 * Name: vchi_msg_remove
66 * Arguments: struct vchi_service_handle *handle,
68 * Description: Routine to remove a message (after it has been read with
71 * Returns: int32_t - success == 0
73 ***********************************************************/
74 int32_t vchi_msg_remove(struct vchi_service_handle *handle)
76 struct shim_service *service = (struct shim_service *)handle;
77 struct vchiq_header *header;
79 header = vchiu_queue_pop(&service->queue);
81 vchiq_release_message(service->handle, header);
85 EXPORT_SYMBOL(vchi_msg_remove);
87 /***********************************************************
88 * Name: vchi_msg_queue
90 * Arguments: struct vchi_service_handle *handle,
91 * ssize_t (*copy_callback)(void *context, void *dest,
92 * size_t offset, size_t maxsize),
96 * Description: Thin wrapper to queue a message onto a connection
98 * Returns: int32_t - success == 0
100 ***********************************************************/
102 int32_t vchi_msg_queue(struct vchi_service_handle *handle, void *context,
105 struct shim_service *service = (struct shim_service *)handle;
106 enum vchiq_status status;
109 status = vchiq_queue_kernel_message(service->handle, context,
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
117 if (status != VCHIQ_RETRY)
123 return vchiq_status_to_vchi(status);
126 int vchi_queue_kernel_message(struct vchi_service_handle *handle, void *data,
129 return vchi_msg_queue(handle, data, size);
131 EXPORT_SYMBOL(vchi_queue_kernel_message);
133 /***********************************************************
134 * Name: vchi_bulk_queue_receive
136 * Arguments: VCHI_BULK_HANDLE_T handle,
138 * const uint32_t data_size,
139 * enum vchi_flags flags
142 * Description: Routine to setup a rcv buffer
144 * Returns: int32_t - success == 0
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,
151 struct shim_service *service = (struct shim_service *)handle;
152 enum vchiq_bulk_mode mode;
153 enum vchiq_status status;
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;
161 case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
162 mode = VCHIQ_BULK_MODE_BLOCKING;
164 case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
165 case VCHI_FLAGS_NONE:
166 mode = VCHIQ_BULK_MODE_NOCALLBACK;
169 WARN(1, "unsupported message\n");
170 return vchiq_status_to_vchi(VCHIQ_ERROR);
174 status = vchiq_bulk_receive(service->handle, data_dst,
175 data_size, bulk_handle, mode);
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
181 if (status != VCHIQ_RETRY)
187 return vchiq_status_to_vchi(status);
189 EXPORT_SYMBOL(vchi_bulk_queue_receive);
191 /***********************************************************
192 * Name: vchi_bulk_queue_transmit
194 * Arguments: VCHI_BULK_HANDLE_T handle,
195 * const void *data_src,
196 * uint32_t data_size,
197 * enum vchi_flags flags,
200 * Description: Routine to transmit some data
202 * Returns: int32_t - success == 0
204 ***********************************************************/
205 int32_t vchi_bulk_queue_transmit(struct vchi_service_handle *handle,
206 const void *data_src,
208 enum vchi_flags flags,
211 struct shim_service *service = (struct shim_service *)handle;
212 enum vchiq_bulk_mode mode;
213 enum vchiq_status status;
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;
221 case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
222 case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
223 mode = VCHIQ_BULK_MODE_BLOCKING;
225 case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
226 case VCHI_FLAGS_NONE:
227 mode = VCHIQ_BULK_MODE_NOCALLBACK;
230 WARN(1, "unsupported message\n");
231 return vchiq_status_to_vchi(VCHIQ_ERROR);
235 status = vchiq_bulk_transmit(service->handle, data_src,
236 data_size, bulk_handle, mode);
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
243 if (status != VCHIQ_RETRY)
249 return vchiq_status_to_vchi(status);
251 EXPORT_SYMBOL(vchi_bulk_queue_transmit);
253 /***********************************************************
254 * Name: vchi_msg_dequeue
256 * Arguments: struct vchi_service_handle *handle,
258 * uint32_t max_data_size_to_read,
259 * uint32_t *actual_msg_size
260 * enum vchi_flags flags
262 * Description: Routine to dequeue a message into the supplied buffer
264 * Returns: int32_t - success == 0
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)
271 struct shim_service *service = (struct shim_service *)handle;
272 struct vchiq_header *header;
274 WARN_ON((flags != VCHI_FLAGS_NONE) &&
275 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
277 if (flags == VCHI_FLAGS_NONE)
278 if (vchiu_queue_is_empty(&service->queue))
281 header = vchiu_queue_pop(&service->queue);
283 memcpy(data, header->data, header->size < max_data_size_to_read ?
284 header->size : max_data_size_to_read);
286 *actual_msg_size = header->size;
288 vchiq_release_message(service->handle, header);
292 EXPORT_SYMBOL(vchi_msg_dequeue);
294 /***********************************************************
295 * Name: vchi_held_msg_release
297 * Arguments: struct vchi_held_msg *message
299 * Description: Routine to release a held message (after it has been read with
302 * Returns: int32_t - success == 0
304 ***********************************************************/
305 int32_t vchi_held_msg_release(struct vchi_held_msg *message)
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
315 vchiq_release_message((unsigned int)(long)message->service,
316 (struct vchiq_header *)message->message);
320 EXPORT_SYMBOL(vchi_held_msg_release);
322 /***********************************************************
323 * Name: vchi_msg_hold
325 * Arguments: struct vchi_service_handle *handle,
327 * uint32_t *msg_size,
328 * enum vchi_flags flags,
329 * struct vchi_held_msg *message_handle
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
336 * Returns: int32_t - success == 0
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)
343 struct shim_service *service = (struct shim_service *)handle;
344 struct vchiq_header *header;
346 WARN_ON((flags != VCHI_FLAGS_NONE) &&
347 (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE));
349 if (flags == VCHI_FLAGS_NONE)
350 if (vchiu_queue_is_empty(&service->queue))
353 header = vchiu_queue_pop(&service->queue);
355 *data = header->data;
356 *msg_size = header->size;
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
366 message_handle->service =
367 (struct opaque_vchi_service_t *)(long)service->handle;
368 message_handle->message = header;
372 EXPORT_SYMBOL(vchi_msg_hold);
374 /***********************************************************
375 * Name: vchi_initialise
377 * Arguments: struct vchi_instance_handle **instance_handle
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
383 * Returns: 0 if successful, failure otherwise
385 ***********************************************************/
387 int32_t vchi_initialise(struct vchi_instance_handle **instance_handle)
389 struct vchiq_instance *instance;
390 enum vchiq_status status;
392 status = vchiq_initialise(&instance);
394 *instance_handle = (struct vchi_instance_handle *)instance;
396 return vchiq_status_to_vchi(status);
398 EXPORT_SYMBOL(vchi_initialise);
400 /***********************************************************
403 * Arguments: struct vchi_instance_handle *instance_handle
405 * Description: Starts the command service on each connection,
406 * causing INIT messages to be pinged back and forth
408 * Returns: 0 if successful, failure otherwise
410 ***********************************************************/
411 int32_t vchi_connect(struct vchi_instance_handle *instance_handle)
413 struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
415 return vchiq_connect(instance);
417 EXPORT_SYMBOL(vchi_connect);
419 /***********************************************************
420 * Name: vchi_disconnect
422 * Arguments: struct vchi_instance_handle *instance_handle
424 * Description: Stops the command service on each connection,
425 * causing DE-INIT messages to be pinged back and forth
427 * Returns: 0 if successful, failure otherwise
429 ***********************************************************/
430 int32_t vchi_disconnect(struct vchi_instance_handle *instance_handle)
432 struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
434 return vchiq_status_to_vchi(vchiq_shutdown(instance));
436 EXPORT_SYMBOL(vchi_disconnect);
438 /***********************************************************
439 * Name: vchi_service_open
440 * Name: vchi_service_create
442 * Arguments: struct vchi_instance_handle *instance_handle
443 * struct service_creation *setup,
444 * struct vchi_service_handle **handle
446 * Description: Routine to open a service
448 * Returns: int32_t - success == 0
450 ***********************************************************/
452 static enum vchiq_status shim_callback(enum vchiq_reason reason,
453 struct vchiq_header *header,
457 struct shim_service *service =
458 (struct shim_service *)VCHIQ_GET_SERVICE_USERDATA(handle);
460 if (!service->callback)
464 case VCHIQ_MESSAGE_AVAILABLE:
465 vchiu_queue_push(&service->queue, header);
467 service->callback(service->callback_param,
468 VCHI_CALLBACK_MSG_AVAILABLE, NULL);
472 case VCHIQ_BULK_TRANSMIT_DONE:
473 service->callback(service->callback_param,
474 VCHI_CALLBACK_BULK_SENT, bulk_user);
477 case VCHIQ_BULK_RECEIVE_DONE:
478 service->callback(service->callback_param,
479 VCHI_CALLBACK_BULK_RECEIVED, bulk_user);
482 case VCHIQ_SERVICE_CLOSED:
483 service->callback(service->callback_param,
484 VCHI_CALLBACK_SERVICE_CLOSED, NULL);
487 case VCHIQ_SERVICE_OPENED:
488 /* No equivalent VCHI reason */
491 case VCHIQ_BULK_TRANSMIT_ABORTED:
492 service->callback(service->callback_param,
493 VCHI_CALLBACK_BULK_TRANSMIT_ABORTED,
497 case VCHIQ_BULK_RECEIVE_ABORTED:
498 service->callback(service->callback_param,
499 VCHI_CALLBACK_BULK_RECEIVE_ABORTED,
504 WARN(1, "not supported\n");
509 vchiq_release_message(service->handle, header);
511 return VCHIQ_SUCCESS;
514 static struct shim_service *service_alloc(struct vchiq_instance *instance,
515 struct service_creation *setup)
517 struct shim_service *service = kzalloc(sizeof(struct shim_service), GFP_KERNEL);
522 if (!vchiu_queue_init(&service->queue, 64)) {
523 service->callback = setup->callback;
524 service->callback_param = setup->callback_param;
534 static void service_free(struct shim_service *service)
537 vchiu_queue_delete(&service->queue);
542 int32_t vchi_service_open(struct vchi_instance_handle *instance_handle,
543 struct service_creation *setup,
544 struct vchi_service_handle **handle)
546 struct vchiq_instance *instance = (struct vchiq_instance *)instance_handle;
547 struct shim_service *service = service_alloc(instance, setup);
549 *handle = (struct vchi_service_handle *)service;
552 struct vchiq_service_params params;
553 enum vchiq_status status;
555 memset(¶ms, 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;
562 status = vchiq_open_service(instance, ¶ms,
564 if (status != VCHIQ_SUCCESS) {
565 service_free(service);
571 return service ? 0 : -1;
573 EXPORT_SYMBOL(vchi_service_open);
575 int32_t vchi_service_close(const struct vchi_service_handle *handle)
578 struct shim_service *service = (struct shim_service *)handle;
581 enum vchiq_status status = vchiq_close_service(service->handle);
582 if (status == VCHIQ_SUCCESS)
583 service_free(service);
585 ret = vchiq_status_to_vchi(status);
589 EXPORT_SYMBOL(vchi_service_close);
591 int32_t vchi_service_set_option(const struct vchi_service_handle *handle,
592 enum vchi_service_option option,
596 struct shim_service *service = (struct shim_service *)handle;
597 enum vchiq_service_option vchiq_option;
600 case VCHI_SERVICE_OPTION_TRACE:
601 vchiq_option = VCHIQ_SERVICE_OPTION_TRACE;
603 case VCHI_SERVICE_OPTION_SYNCHRONOUS:
604 vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS;
611 enum vchiq_status status =
612 vchiq_set_service_option(service->handle,
616 ret = vchiq_status_to_vchi(status);
620 EXPORT_SYMBOL(vchi_service_set_option);
622 int32_t vchi_get_peer_version(const struct vchi_service_handle *handle, short *peer_version)
625 struct shim_service *service = (struct shim_service *)handle;
628 enum vchiq_status status;
630 status = vchiq_get_peer_version(service->handle, peer_version);
631 ret = vchiq_status_to_vchi(status);
635 EXPORT_SYMBOL(vchi_get_peer_version);
637 /***********************************************************
638 * Name: vchi_service_use
640 * Arguments: const struct vchi_service_handle *handle
642 * Description: Routine to increment refcount on a service
646 ***********************************************************/
647 int32_t vchi_service_use(const struct vchi_service_handle *handle)
651 struct shim_service *service = (struct shim_service *)handle;
653 ret = vchiq_status_to_vchi(vchiq_use_service(service->handle));
656 EXPORT_SYMBOL(vchi_service_use);
658 /***********************************************************
659 * Name: vchi_service_release
661 * Arguments: const struct vchi_service_handle *handle
663 * Description: Routine to decrement refcount on a service
667 ***********************************************************/
668 int32_t vchi_service_release(const struct vchi_service_handle *handle)
672 struct shim_service *service = (struct shim_service *)handle;
674 ret = vchiq_status_to_vchi(
675 vchiq_release_service(service->handle));
678 EXPORT_SYMBOL(vchi_service_release);