1/*
2Copyright (c) 2012-2014, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <unistd.h>
29#include <fcntl.h>
30#include <sys/ioctl.h>
31#include <stdio.h>
32
33#include "vchiq.h"
34#include "vchiq_cfg.h"
35#include "vchiq_ioctl.h"
36#include "interface/vchi/vchi.h"
37#include "interface/vchi/common/endian.h"
38#include "interface/vcos/vcos.h"
39
40#define IS_POWER_2(x) ((x & (x - 1)) == 0)
41#define VCHIQ_MAX_INSTANCE_SERVICES 32
42#define MSGBUF_SIZE (VCHIQ_MAX_MSG_SIZE + sizeof(VCHIQ_HEADER_T))
43
44#define RETRY(r,x) do { r = x; } while ((r == -1) && (errno == EINTR))
45
46#define VCOS_LOG_CATEGORY (&vchiq_lib_log_category)
47
48typedef struct vchiq_service_struct
49{
50 VCHIQ_SERVICE_BASE_T base;
51 VCHIQ_SERVICE_HANDLE_T handle;
52 VCHIQ_SERVICE_HANDLE_T lib_handle;
53 int fd;
54 VCHI_CALLBACK_T vchi_callback;
55 void *peek_buf;
56 int peek_size;
57 int client_id;
58 char is_client;
59} VCHIQ_SERVICE_T;
60
61typedef struct vchiq_service_struct VCHI_SERVICE_T;
62
63struct vchiq_instance_struct
64{
65 int fd;
66 int initialised;
67 int connected;
68 int use_close_delivered;
69 VCOS_THREAD_T completion_thread;
70 VCOS_MUTEX_T mutex;
71 int used_services;
72 VCHIQ_SERVICE_T services[VCHIQ_MAX_INSTANCE_SERVICES];
73} vchiq_instance;
74
75typedef struct vchiq_instance_struct VCHI_STATE_T;
76
77/* Local data */
78static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN;
79static VCOS_LOG_CAT_T vchiq_lib_log_category;
80static VCOS_MUTEX_T vchiq_lib_mutex;
81static void *free_msgbufs;
82static unsigned int handle_seq;
83
84vcos_static_assert(IS_POWER_2(VCHIQ_MAX_INSTANCE_SERVICES));
85
86/* Local utility functions */
87static VCHIQ_INSTANCE_T
88vchiq_lib_init(const int dev_vchiq_fd);
89
90static void *completion_thread(void *);
91
92static VCHIQ_STATUS_T
93create_service(VCHIQ_INSTANCE_T instance,
94 const VCHIQ_SERVICE_PARAMS_T *params,
95 VCHI_CALLBACK_T vchi_callback,
96 int is_open,
97 VCHIQ_SERVICE_HANDLE_T *phandle);
98
99static int
100fill_peek_buf(VCHI_SERVICE_T *service,
101 VCHI_FLAGS_T flags);
102
103static void *
104alloc_msgbuf(void);
105
106static void
107free_msgbuf(void *buf);
108
109static __inline int
110is_valid_instance(VCHIQ_INSTANCE_T instance)
111{
112 return (instance == &vchiq_instance) && (instance->initialised > 0);
113}
114
115static inline VCHIQ_SERVICE_T *
116handle_to_service(VCHIQ_SERVICE_HANDLE_T handle)
117{
118 return &vchiq_instance.services[handle & (VCHIQ_MAX_INSTANCE_SERVICES - 1)];
119}
120
121static VCHIQ_SERVICE_T *
122find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle)
123{
124 VCHIQ_SERVICE_T *service;
125
126 service = handle_to_service(handle);
127 if (service && (service->lib_handle != handle))
128 service = NULL;
129
130 if (!service)
131 vcos_log_info("Invalid service handle 0x%x", handle);
132
133 return service;
134}
135
136/*
137 * VCHIQ API
138 */
139
140// If dev_vchiq_fd == -1 then /dev/vchiq will be opened by this fn (as normal)
141//
142// Otherwise the given fd will be used. N.B. in this case the fd is duped
143// so the caller will probably want to close whatever fd was passed once
144// this call has returned. This slightly odd behaviour makes shutdown and
145// error cases much simpler.
146VCHIQ_STATUS_T
147vchiq_initialise_fd(VCHIQ_INSTANCE_T *pinstance, int dev_vchiq_fd)
148{
149 VCHIQ_INSTANCE_T instance;
150
151 instance = vchiq_lib_init(dev_vchiq_fd);
152
153 vcos_log_trace( "%s: returning instance handle %p", __func__, instance );
154
155 *pinstance = instance;
156
157 return (instance != NULL) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
158}
159
160VCHIQ_STATUS_T
161vchiq_initialise(VCHIQ_INSTANCE_T *pinstance)
162{
163 return vchiq_initialise_fd(pinstance, -1);
164}
165
166VCHIQ_STATUS_T
167vchiq_shutdown(VCHIQ_INSTANCE_T instance)
168{
169 vcos_log_trace( "%s called", __func__ );
170
171 if (!is_valid_instance(instance))
172 return VCHIQ_ERROR;
173
174 vcos_mutex_lock(&instance->mutex);
175
176 if (instance->initialised == 1)
177 {
178 int i;
179
180 instance->initialised = -1; /* Enter limbo */
181
182 /* Remove all services */
183
184 for (i = 0; i < instance->used_services; i++)
185 {
186 if (instance->services[i].lib_handle != VCHIQ_SERVICE_HANDLE_INVALID)
187 {
188 vchiq_remove_service(instance->services[i].lib_handle);
189 instance->services[i].lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
190 }
191 }
192
193 if (instance->connected)
194 {
195 int ret;
196 RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_SHUTDOWN, 0));
197 vcos_assert(ret == 0);
198 vcos_thread_join(&instance->completion_thread, NULL);
199 instance->connected = 0;
200 }
201
202 close(instance->fd);
203 instance->fd = -1;
204 }
205 else if (instance->initialised > 1)
206 {
207 instance->initialised--;
208 }
209
210 vcos_mutex_unlock(&instance->mutex);
211
212 vcos_global_lock();
213
214 if (instance->initialised == -1)
215 {
216 vcos_mutex_delete(&instance->mutex);
217 instance->initialised = 0;
218 }
219
220 vcos_global_unlock();
221
222 vcos_log_trace( "%s returning", __func__ );
223
224 vcos_log_unregister(&vchiq_lib_log_category);
225
226 return VCHIQ_SUCCESS;
227}
228
229VCHIQ_STATUS_T
230vchiq_connect(VCHIQ_INSTANCE_T instance)
231{
232 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
233 VCOS_THREAD_ATTR_T attrs;
234 int ret;
235
236 vcos_log_trace( "%s called", __func__ );
237
238 if (!is_valid_instance(instance))
239 return VCHIQ_ERROR;
240
241 vcos_mutex_lock(&instance->mutex);
242
243 if (instance->connected)
244 goto out;
245
246 ret = ioctl(instance->fd, VCHIQ_IOC_CONNECT, 0);
247 if (ret != 0)
248 {
249 status = VCHIQ_ERROR;
250 goto out;
251 }
252
253 vcos_thread_attr_init(&attrs);
254 if (vcos_thread_create(&instance->completion_thread, "VCHIQ completion",
255 &attrs, completion_thread, instance) != VCOS_SUCCESS)
256 {
257 status = VCHIQ_ERROR;
258 goto out;
259 }
260
261 instance->connected = 1;
262
263out:
264 vcos_mutex_unlock(&instance->mutex);
265 return status;
266}
267
268VCHIQ_STATUS_T
269vchiq_add_service(VCHIQ_INSTANCE_T instance,
270 const VCHIQ_SERVICE_PARAMS_T *params,
271 VCHIQ_SERVICE_HANDLE_T *phandle)
272{
273 VCHIQ_STATUS_T status;
274
275 vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)",
276 __func__,
277 params->fourcc,
278 (params->fourcc >> 24) & 0xff,
279 (params->fourcc >> 16) & 0xff,
280 (params->fourcc >> 8) & 0xff,
281 (params->fourcc ) & 0xff );
282
283 if (!params->callback)
284 return VCHIQ_ERROR;
285
286 if (!is_valid_instance(instance))
287 return VCHIQ_ERROR;
288
289 status = create_service(instance,
290 params,
291 NULL/*vchi_callback*/,
292 0/*!open*/,
293 phandle);
294
295 vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*phandle );
296
297 return status;
298}
299
300VCHIQ_STATUS_T
301vchiq_open_service(VCHIQ_INSTANCE_T instance,
302 const VCHIQ_SERVICE_PARAMS_T *params,
303 VCHIQ_SERVICE_HANDLE_T *phandle)
304{
305 VCHIQ_STATUS_T status;
306
307 vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)",
308 __func__,
309 params->fourcc,
310 (params->fourcc >> 24) & 0xff,
311 (params->fourcc >> 16) & 0xff,
312 (params->fourcc >> 8) & 0xff,
313 (params->fourcc ) & 0xff );
314
315 if (!params->callback)
316 return VCHIQ_ERROR;
317
318 if (!is_valid_instance(instance))
319 return VCHIQ_ERROR;
320
321 status = create_service(instance,
322 params,
323 NULL/*vchi_callback*/,
324 1/*open*/,
325 phandle);
326
327 vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*phandle );
328
329 return status;
330}
331
332VCHIQ_STATUS_T
333vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle)
334{
335 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
336 int ret;
337
338 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
339
340 if (!service)
341 return VCHIQ_ERROR;
342
343 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle));
344
345 if (service->is_client)
346 service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
347
348 if (ret != 0)
349 return VCHIQ_ERROR;
350
351 return VCHIQ_SUCCESS;
352}
353
354VCHIQ_STATUS_T
355vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle)
356{
357 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
358 int ret;
359
360 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
361
362 if (!service)
363 return VCHIQ_ERROR;
364
365 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle));
366
367 service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
368
369 if (ret != 0)
370 return VCHIQ_ERROR;
371
372 return VCHIQ_SUCCESS;
373}
374
375VCHIQ_STATUS_T
376vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
377 const VCHIQ_ELEMENT_T *elements,
378 int count)
379{
380 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
381 VCHIQ_QUEUE_MESSAGE_T args;
382 int ret;
383
384 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
385
386 if (!service)
387 return VCHIQ_ERROR;
388
389 args.handle = service->handle;
390 args.elements = elements;
391 args.count = count;
392 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
393
394 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
395}
396
397void
398vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle,
399 VCHIQ_HEADER_T *header)
400{
401 vcos_log_trace( "%s handle=%08x, header=%x", __func__, (uint32_t)handle, (uint32_t)header );
402
403 free_msgbuf(header);
404}
405
406VCHIQ_STATUS_T
407vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
408 const void *data,
409 int size,
410 void *userdata)
411{
412 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
413 VCHIQ_QUEUE_BULK_TRANSFER_T args;
414 int ret;
415
416 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
417
418 if (!service)
419 return VCHIQ_ERROR;
420
421 args.handle = service->handle;
422 args.data = (void *)data;
423 args.size = size;
424 args.userdata = userdata;
425 args.mode = VCHIQ_BULK_MODE_CALLBACK;
426 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
427
428 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
429}
430
431VCHIQ_STATUS_T
432vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,
433 void *data,
434 int size,
435 void *userdata)
436{
437 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
438 VCHIQ_QUEUE_BULK_TRANSFER_T args;
439 int ret;
440
441 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
442
443 if (!service)
444 return VCHIQ_ERROR;
445
446 args.handle = service->handle;
447 args.data = data;
448 args.size = size;
449 args.userdata = userdata;
450 args.mode = VCHIQ_BULK_MODE_CALLBACK;
451 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
452
453 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
454}
455
456VCHIQ_STATUS_T
457vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
458 VCHI_MEM_HANDLE_T memhandle,
459 const void *offset,
460 int size,
461 void *userdata)
462{
463 vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
464
465 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
466
467 return vchiq_queue_bulk_transmit(handle, offset, size, userdata);
468}
469
470VCHIQ_STATUS_T
471vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
472 VCHI_MEM_HANDLE_T memhandle,
473 void *offset,
474 int size,
475 void *userdata)
476{
477 vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
478
479 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
480
481 return vchiq_queue_bulk_receive(handle, offset, size, userdata);
482}
483
484VCHIQ_STATUS_T
485vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle,
486 const void *data,
487 int size,
488 void *userdata,
489 VCHIQ_BULK_MODE_T mode)
490{
491 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
492 VCHIQ_QUEUE_BULK_TRANSFER_T args;
493 int ret;
494
495 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
496
497 if (!service)
498 return VCHIQ_ERROR;
499
500 args.handle = service->handle;
501 args.data = (void *)data;
502 args.size = size;
503 args.userdata = userdata;
504 args.mode = mode;
505 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
506
507 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
508}
509
510VCHIQ_STATUS_T
511vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle,
512 void *data,
513 int size,
514 void *userdata,
515 VCHIQ_BULK_MODE_T mode)
516{
517 return vchiq_bulk_receive_handle(handle, VCHI_MEM_HANDLE_INVALID, data, size, userdata, mode, NULL);
518}
519
520VCHIQ_STATUS_T
521vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle,
522 VCHI_MEM_HANDLE_T memhandle,
523 const void *offset,
524 int size,
525 void *userdata,
526 VCHIQ_BULK_MODE_T mode)
527{
528 vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
529
530 return vchiq_bulk_transmit(handle, offset, size, userdata, mode);
531}
532
533VCHIQ_STATUS_T
534vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle,
535 VCHI_MEM_HANDLE_T memhandle,
536 void *offset,
537 int size,
538 void *userdata,
539 VCHIQ_BULK_MODE_T mode,
540 int (*copy_pagelist)())
541{
542 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
543 VCHIQ_QUEUE_BULK_TRANSFER_T args;
544 int ret;
545
546 vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID);
547
548 vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle );
549
550 if (!service)
551 return VCHIQ_ERROR;
552
553 args.handle = service->handle;
554 args.data = offset;
555 args.size = size;
556 args.userdata = userdata;
557 args.mode = mode;
558 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
559
560 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
561}
562
563int
564vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle)
565{
566 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
567
568 if (!service)
569 return VCHIQ_ERROR;
570
571 return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle);
572}
573
574void *
575vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle)
576{
577 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
578
579 return service ? service->base.userdata : NULL;
580}
581
582int
583vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle)
584{
585 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
586
587 return service ? service->base.fourcc : 0;
588}
589
590VCHIQ_STATUS_T
591vchiq_get_config(VCHIQ_INSTANCE_T instance,
592 int config_size,
593 VCHIQ_CONFIG_T *pconfig)
594{
595 VCHIQ_GET_CONFIG_T args;
596 int ret;
597
598 if (!is_valid_instance(instance))
599 return VCHIQ_ERROR;
600
601 args.config_size = config_size;
602 args.pconfig = pconfig;
603
604 RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args));
605
606 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
607}
608
609int32_t
610vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle )
611{
612 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
613 int ret;
614
615 if (!service)
616 return VCHIQ_ERROR;
617
618 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle));
619 return ret;
620}
621
622int32_t
623vchiq_release_service( const VCHIQ_SERVICE_HANDLE_T handle )
624{
625 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
626 int ret;
627
628 if (!service)
629 return VCHIQ_ERROR;
630
631 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle));
632 return ret;
633}
634
635VCHIQ_STATUS_T
636vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle,
637 VCHIQ_SERVICE_OPTION_T option, int value)
638{
639 VCHIQ_SET_SERVICE_OPTION_T args;
640 VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
641 int ret;
642
643 if (!service)
644 return VCHIQ_ERROR;
645
646 args.handle = service->handle;
647 args.option = option;
648 args.value = value;
649
650 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args));
651
652 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
653}
654
655/*
656 * VCHI API
657 */
658
659/* ----------------------------------------------------------------------
660 * return pointer to the mphi message driver function table
661 * -------------------------------------------------------------------- */
662const VCHI_MESSAGE_DRIVER_T *
663vchi_mphi_message_driver_func_table( void )
664{
665 return NULL;
666}
667
668/* ----------------------------------------------------------------------
669 * return a pointer to the 'single' connection driver fops
670 * -------------------------------------------------------------------- */
671const VCHI_CONNECTION_API_T *
672single_get_func_table( void )
673{
674 return NULL;
675}
676
677VCHI_CONNECTION_T *
678vchi_create_connection( const VCHI_CONNECTION_API_T * function_table,
679 const VCHI_MESSAGE_DRIVER_T * low_level )
680{
681 vcos_unused(function_table);
682 vcos_unused(low_level);
683
684 return NULL;
685}
686
687/***********************************************************
688 * Name: vchi_msg_peek
689 *
690 * Arguments: const VCHI_SERVICE_HANDLE_T handle,
691 * void **data,
692 * uint32_t *msg_size,
693 * VCHI_FLAGS_T flags
694 *
695 * Description: Routine to return a pointer to the current message (to allow in place processing)
696 * The message can be removed using vchi_msg_remove when you're finished
697 *
698 * Returns: int32_t - success == 0
699 *
700 ***********************************************************/
701int32_t
702vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle,
703 void **data,
704 uint32_t *msg_size,
705 VCHI_FLAGS_T flags )
706{
707 VCHI_SERVICE_T *service = find_service_by_handle(handle);
708 int ret;
709
710 if (!service)
711 return VCHIQ_ERROR;
712
713 ret = fill_peek_buf(service, flags);
714
715 if (ret == 0)
716 {
717 *data = service->peek_buf;
718 *msg_size = service->peek_size;
719 }
720
721 return ret;
722}
723
724/***********************************************************
725 * Name: vchi_msg_remove
726 *
727 * Arguments: const VCHI_SERVICE_HANDLE_T handle,
728 *
729 * Description: Routine to remove a message (after it has been read with vchi_msg_peek)
730 *
731 * Returns: int32_t - success == 0
732 *
733 ***********************************************************/
734int32_t
735vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle )
736{
737 VCHI_SERVICE_T *service = find_service_by_handle(handle);
738
739 if (!service)
740 return VCHIQ_ERROR;
741
742 /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */
743 vcos_assert(service->peek_size >= 0);
744
745 /* Invalidate the content but reuse the buffer */
746 service->peek_size = -1;
747
748 return 0;
749}
750
751/***********************************************************
752 * Name: vchi_msg_queue
753 *
754 * Arguments: VCHI_SERVICE_HANDLE_T handle,
755 * const void *data,
756 * uint32_t data_size,
757 * VCHI_FLAGS_T flags,
758 * void *msg_handle,
759 *
760 * Description: Thin wrapper to queue a message onto a connection
761 *
762 * Returns: int32_t - success == 0
763 *
764 ***********************************************************/
765int32_t
766vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle,
767 const void * data,
768 uint32_t data_size,
769 VCHI_FLAGS_T flags,
770 void * msg_handle )
771{
772 VCHI_SERVICE_T *service = find_service_by_handle(handle);
773 VCHIQ_QUEUE_MESSAGE_T args;
774 VCHIQ_ELEMENT_T element = {data, data_size};
775 int ret;
776
777 vcos_unused(msg_handle);
778 vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
779
780 if (!service)
781 return VCHIQ_ERROR;
782
783 args.handle = service->handle;
784 args.elements = &element;
785 args.count = 1;
786 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
787
788 return ret;
789}
790
791/***********************************************************
792 * Name: vchi_bulk_queue_receive
793 *
794 * Arguments: VCHI_BULK_HANDLE_T handle,
795 * void *data_dst,
796 * const uint32_t data_size,
797 * VCHI_FLAGS_T flags
798 * void *bulk_handle
799 *
800 * Description: Routine to setup a rcv buffer
801 *
802 * Returns: int32_t - success == 0
803 *
804 ***********************************************************/
805int32_t
806vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle,
807 void * data_dst,
808 uint32_t data_size,
809 VCHI_FLAGS_T flags,
810 void * bulk_handle )
811{
812 VCHI_SERVICE_T *service = find_service_by_handle(handle);
813 VCHIQ_QUEUE_BULK_TRANSFER_T args;
814 int ret;
815
816 if (!service)
817 return VCHIQ_ERROR;
818
819 switch ((int)flags) {
820 case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
821 args.mode = VCHIQ_BULK_MODE_CALLBACK;
822 break;
823 case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
824 args.mode = VCHIQ_BULK_MODE_BLOCKING;
825 break;
826 case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
827 case VCHI_FLAGS_NONE:
828 args.mode = VCHIQ_BULK_MODE_NOCALLBACK;
829 break;
830 default:
831 vcos_assert(0);
832 break;
833 }
834
835 args.handle = service->handle;
836 args.data = data_dst;
837 args.size = data_size;
838 args.userdata = bulk_handle;
839 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args));
840
841 return ret;
842}
843
844/***********************************************************
845 * Name: vchi_bulk_queue_transmit
846 *
847 * Arguments: VCHI_BULK_HANDLE_T handle,
848 * const void *data_src,
849 * uint32_t data_size,
850 * VCHI_FLAGS_T flags,
851 * void *bulk_handle
852 *
853 * Description: Routine to transmit some data
854 *
855 * Returns: int32_t - success == 0
856 *
857 ***********************************************************/
858int32_t
859vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle,
860 const void * data_src,
861 uint32_t data_size,
862 VCHI_FLAGS_T flags,
863 void * bulk_handle )
864{
865 VCHI_SERVICE_T *service = find_service_by_handle(handle);
866 VCHIQ_QUEUE_BULK_TRANSFER_T args;
867 int ret;
868
869 if (!service)
870 return VCHIQ_ERROR;
871
872 switch ((int)flags) {
873 case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
874 args.mode = VCHIQ_BULK_MODE_CALLBACK;
875 break;
876 case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ:
877 case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE:
878 args.mode = VCHIQ_BULK_MODE_BLOCKING;
879 break;
880 case VCHI_FLAGS_BLOCK_UNTIL_QUEUED:
881 case VCHI_FLAGS_NONE:
882 args.mode = VCHIQ_BULK_MODE_NOCALLBACK;
883 break;
884 default:
885 vcos_assert(0);
886 break;
887 }
888
889 args.handle = service->handle;
890 args.data = (void *)data_src;
891 args.size = data_size;
892 args.userdata = bulk_handle;
893 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args));
894
895 return ret;
896}
897
898/***********************************************************
899 * Name: vchi_msg_dequeue
900 *
901 * Arguments: VCHI_SERVICE_HANDLE_T handle,
902 * void *data,
903 * uint32_t max_data_size_to_read,
904 * uint32_t *actual_msg_size
905 * VCHI_FLAGS_T flags
906 *
907 * Description: Routine to dequeue a message into the supplied buffer
908 *
909 * Returns: int32_t - success == 0
910 *
911 ***********************************************************/
912int32_t
913vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle,
914 void *data,
915 uint32_t max_data_size_to_read,
916 uint32_t *actual_msg_size,
917 VCHI_FLAGS_T flags )
918{
919 VCHI_SERVICE_T *service = find_service_by_handle(handle);
920 VCHIQ_DEQUEUE_MESSAGE_T args;
921 int ret;
922
923 vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
924
925 if (!service)
926 return VCHIQ_ERROR;
927
928 if (service->peek_size >= 0)
929 {
930 vcos_log_error("vchi_msg_dequeue -> using peek buffer\n");
931 if ((uint32_t)service->peek_size <= max_data_size_to_read)
932 {
933 memcpy(data, service->peek_buf, service->peek_size);
934 *actual_msg_size = service->peek_size;
935 /* Invalidate the peek data, but retain the buffer */
936 service->peek_size = -1;
937 ret = 0;
938 }
939 else
940 {
941 ret = -1;
942 }
943 }
944 else
945 {
946 args.handle = service->handle;
947 args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
948 args.bufsize = max_data_size_to_read;
949 args.buf = data;
950 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args));
951 if (ret >= 0)
952 {
953 *actual_msg_size = ret;
954 ret = 0;
955 }
956 }
957
958 if ((ret < 0) && (errno != EWOULDBLOCK))
959 fprintf(stderr, "vchi_msg_dequeue -> %d(%d)\n", ret, errno);
960
961 return ret;
962}
963
964/***********************************************************
965 * Name: vchi_msg_queuev
966 *
967 * Arguments: VCHI_SERVICE_HANDLE_T handle,
968 * const void *data,
969 * uint32_t data_size,
970 * VCHI_FLAGS_T flags,
971 * void *msg_handle
972 *
973 * Description: Thin wrapper to queue a message onto a connection
974 *
975 * Returns: int32_t - success == 0
976 *
977 ***********************************************************/
978
979vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T));
980vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data));
981vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size));
982
983int32_t
984vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle,
985 VCHI_MSG_VECTOR_T * vector,
986 uint32_t count,
987 VCHI_FLAGS_T flags,
988 void *msg_handle )
989{
990 VCHI_SERVICE_T *service = find_service_by_handle(handle);
991 VCHIQ_QUEUE_MESSAGE_T args;
992 int ret;
993
994 vcos_unused(msg_handle);
995
996 vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED);
997
998 if (!service)
999 return VCHIQ_ERROR;
1000
1001 args.handle = service->handle;
1002 args.elements = (const VCHIQ_ELEMENT_T *)vector;
1003 args.count = count;
1004 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args));
1005
1006 return ret;
1007}
1008
1009/***********************************************************
1010 * Name: vchi_held_msg_release
1011 *
1012 * Arguments: VCHI_HELD_MSG_T *message
1013 *
1014 * Description: Routine to release a held message (after it has been read with vchi_msg_hold)
1015 *
1016 * Returns: int32_t - success == 0
1017 *
1018 ***********************************************************/
1019int32_t
1020vchi_held_msg_release( VCHI_HELD_MSG_T *message )
1021{
1022 int ret = -1;
1023
1024 if (message && message->message && !message->service)
1025 {
1026 free_msgbuf(message->message);
1027 ret = 0;
1028 }
1029
1030 return ret;
1031}
1032
1033/***********************************************************
1034 * Name: vchi_msg_hold
1035 *
1036 * Arguments: VCHI_SERVICE_HANDLE_T handle,
1037 * void **data,
1038 * uint32_t *msg_size,
1039 * VCHI_FLAGS_T flags,
1040 * VCHI_HELD_MSG_T *message_handle
1041 *
1042 * Description: Routine to return a pointer to the current message (to allow in place processing)
1043 * The message is dequeued - don't forget to release the message using
1044 * vchi_held_msg_release when you're finished
1045 *
1046 * Returns: int32_t - success == 0
1047 *
1048 ***********************************************************/
1049int32_t
1050vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle,
1051 void **data,
1052 uint32_t *msg_size,
1053 VCHI_FLAGS_T flags,
1054 VCHI_HELD_MSG_T *message_handle )
1055{
1056 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1057 int ret;
1058
1059 if (!service)
1060 return VCHIQ_ERROR;
1061
1062 ret = fill_peek_buf(service, flags);
1063
1064 if (ret == 0)
1065 {
1066 *data = service->peek_buf;
1067 *msg_size = service->peek_size;
1068
1069 message_handle->message = service->peek_buf;
1070 message_handle->service = NULL;
1071
1072 service->peek_size = -1;
1073 service->peek_buf = NULL;
1074 }
1075
1076 return 0;
1077}
1078
1079/***********************************************************
1080 * Name: vchi_initialise
1081 *
1082 * Arguments: VCHI_INSTANCE_T *instance_handle
1083 * VCHI_CONNECTION_T **connections
1084 * const uint32_t num_connections
1085 *
1086 * Description: Initialises the hardware but does not transmit anything
1087 * When run as a Host App this will be called twice hence the need
1088 * to malloc the state information
1089 *
1090 * Returns: 0 if successful, failure otherwise
1091 *
1092 ***********************************************************/
1093int32_t
1094vchi_initialise( VCHI_INSTANCE_T *instance_handle )
1095{
1096 VCHIQ_INSTANCE_T instance;
1097
1098 instance = vchiq_lib_init(-1);
1099
1100 vcos_log_trace( "%s: returning instance handle %p", __func__, instance );
1101
1102 *instance_handle = (VCHI_INSTANCE_T)instance;
1103
1104 return (instance != NULL) ? 0 : -1;
1105}
1106
1107/***********************************************************
1108 * Name: vchi_connect
1109 *
1110 * Arguments: VCHI_CONNECTION_T **connections
1111 * const uint32_t num_connections
1112 * VCHI_INSTANCE_T instance_handle )
1113 *
1114 * Description: Starts the command service on each connection,
1115 * causing INIT messages to be pinged back and forth
1116 *
1117 * Returns: 0 if successful, failure otherwise
1118 *
1119 ***********************************************************/
1120int32_t
1121vchi_connect( VCHI_CONNECTION_T **connections,
1122 const uint32_t num_connections,
1123 VCHI_INSTANCE_T instance_handle )
1124{
1125 VCHIQ_STATUS_T status;
1126
1127 vcos_unused(connections);
1128 vcos_unused(num_connections);
1129
1130 status = vchiq_connect((VCHIQ_INSTANCE_T)instance_handle);
1131
1132 return (status == VCHIQ_SUCCESS) ? 0 : -1;
1133}
1134
1135
1136/***********************************************************
1137 * Name: vchi_disconnect
1138 *
1139 * Arguments: VCHI_INSTANCE_T instance_handle
1140 *
1141 * Description: Stops the command service on each connection,
1142 * causing DE-INIT messages to be pinged back and forth
1143 *
1144 * Returns: 0 if successful, failure otherwise
1145 *
1146 ***********************************************************/
1147int32_t
1148vchi_disconnect( VCHI_INSTANCE_T instance_handle )
1149{
1150 VCHIQ_STATUS_T status;
1151
1152 status = vchiq_shutdown((VCHIQ_INSTANCE_T)instance_handle);
1153
1154 return (status == VCHIQ_SUCCESS) ? 0 : -1;
1155}
1156
1157
1158/***********************************************************
1159 * Name: vchi_service_open
1160 * Name: vchi_service_create
1161 *
1162 * Arguments: VCHI_INSTANCE_T *instance_handle
1163 * SERVICE_CREATION_T *setup,
1164 * VCHI_SERVICE_HANDLE_T *handle
1165 *
1166 * Description: Routine to open a service
1167 *
1168 * Returns: int32_t - success == 0
1169 *
1170 ***********************************************************/
1171int32_t
1172vchi_service_open( VCHI_INSTANCE_T instance_handle,
1173 SERVICE_CREATION_T *setup,
1174 VCHI_SERVICE_HANDLE_T *handle )
1175{
1176 VCHIQ_SERVICE_PARAMS_T params;
1177 VCHIQ_STATUS_T status;
1178
1179 memset(&params, 0, sizeof(params));
1180 params.fourcc = setup->service_id;
1181 params.userdata = setup->callback_param;
1182 params.version = (short)setup->version.version;
1183 params.version_min = (short)setup->version.version_min;
1184
1185 status = create_service((VCHIQ_INSTANCE_T)instance_handle,
1186 &params,
1187 setup->callback,
1188 1/*open*/,
1189 (VCHIQ_SERVICE_HANDLE_T *)handle);
1190
1191 return (status == VCHIQ_SUCCESS) ? 0 : -1;
1192}
1193
1194int32_t
1195vchi_service_create( VCHI_INSTANCE_T instance_handle,
1196 SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle )
1197{
1198 VCHIQ_SERVICE_PARAMS_T params;
1199 VCHIQ_STATUS_T status;
1200
1201 memset(&params, 0, sizeof(params));
1202 params.fourcc = setup->service_id;
1203 params.userdata = setup->callback_param;
1204 params.version = (short)setup->version.version;
1205 params.version_min = (short)setup->version.version_min;
1206
1207 status = create_service((VCHIQ_INSTANCE_T)instance_handle,
1208 &params,
1209 setup->callback,
1210 0/*!open*/,
1211 (VCHIQ_SERVICE_HANDLE_T *)handle);
1212
1213 return (status == VCHIQ_SUCCESS) ? 0 : -1;
1214}
1215
1216int32_t
1217vchi_service_close( const VCHI_SERVICE_HANDLE_T handle )
1218{
1219 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1220 int ret;
1221
1222 if (!service)
1223 return VCHIQ_ERROR;
1224
1225 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle));
1226
1227 if (service->is_client)
1228 service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
1229
1230 return ret;
1231}
1232
1233int32_t
1234vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle )
1235{
1236 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1237 int ret;
1238
1239 if (!service)
1240 return VCHIQ_ERROR;
1241
1242 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle));
1243
1244 service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
1245
1246 return ret;
1247}
1248
1249/* ----------------------------------------------------------------------
1250 * read a uint32_t from buffer.
1251 * network format is defined to be little endian
1252 * -------------------------------------------------------------------- */
1253uint32_t
1254vchi_readbuf_uint32( const void *_ptr )
1255{
1256 const unsigned char *ptr = _ptr;
1257 return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
1258}
1259
1260/* ----------------------------------------------------------------------
1261 * write a uint32_t to buffer.
1262 * network format is defined to be little endian
1263 * -------------------------------------------------------------------- */
1264void
1265vchi_writebuf_uint32( void *_ptr, uint32_t value )
1266{
1267 unsigned char *ptr = _ptr;
1268 ptr[0] = (unsigned char)((value >> 0) & 0xFF);
1269 ptr[1] = (unsigned char)((value >> 8) & 0xFF);
1270 ptr[2] = (unsigned char)((value >> 16) & 0xFF);
1271 ptr[3] = (unsigned char)((value >> 24) & 0xFF);
1272}
1273
1274/* ----------------------------------------------------------------------
1275 * read a uint16_t from buffer.
1276 * network format is defined to be little endian
1277 * -------------------------------------------------------------------- */
1278uint16_t
1279vchi_readbuf_uint16( const void *_ptr )
1280{
1281 const unsigned char *ptr = _ptr;
1282 return ptr[0] | (ptr[1] << 8);
1283}
1284
1285/* ----------------------------------------------------------------------
1286 * write a uint16_t into the buffer.
1287 * network format is defined to be little endian
1288 * -------------------------------------------------------------------- */
1289void
1290vchi_writebuf_uint16( void *_ptr, uint16_t value )
1291{
1292 unsigned char *ptr = _ptr;
1293 ptr[0] = (value >> 0) & 0xFF;
1294 ptr[1] = (value >> 8) & 0xFF;
1295}
1296
1297/***********************************************************
1298 * Name: vchi_service_use
1299 *
1300 * Arguments: const VCHI_SERVICE_HANDLE_T handle
1301 *
1302 * Description: Routine to increment refcount on a service
1303 *
1304 * Returns: void
1305 *
1306 ***********************************************************/
1307int32_t
1308vchi_service_use( const VCHI_SERVICE_HANDLE_T handle )
1309{
1310 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1311 int ret;
1312
1313 if (!service)
1314 return VCHIQ_ERROR;
1315
1316 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle));
1317 return ret;
1318}
1319
1320/***********************************************************
1321 * Name: vchi_service_release
1322 *
1323 * Arguments: const VCHI_SERVICE_HANDLE_T handle
1324 *
1325 * Description: Routine to decrement refcount on a service
1326 *
1327 * Returns: void
1328 *
1329 ***********************************************************/
1330int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle )
1331{
1332 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1333 int ret;
1334
1335 if (!service)
1336 return VCHIQ_ERROR;
1337
1338 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle));
1339 return ret;
1340}
1341
1342/***********************************************************
1343 * Name: vchi_service_set_option
1344 *
1345 * Arguments: const VCHI_SERVICE_HANDLE_T handle
1346 * VCHI_SERVICE_OPTION_T option
1347 * int value
1348 *
1349 * Description: Routine to set a service control option
1350 *
1351 * Returns: 0 on success, otherwise a non-zero error code
1352 *
1353 ***********************************************************/
1354int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle,
1355 VCHI_SERVICE_OPTION_T option, int value)
1356{
1357 VCHIQ_SET_SERVICE_OPTION_T args;
1358 VCHI_SERVICE_T *service = find_service_by_handle(handle);
1359 int ret;
1360
1361 switch (option)
1362 {
1363 case VCHI_SERVICE_OPTION_TRACE:
1364 args.option = VCHIQ_SERVICE_OPTION_TRACE;
1365 break;
1366 default:
1367 service = NULL;
1368 break;
1369 }
1370
1371 if (!service)
1372 return VCHIQ_ERROR;
1373
1374 args.handle = service->handle;
1375 args.value = value;
1376
1377 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args));
1378
1379 return ret;
1380}
1381
1382/***********************************************************
1383 * Name: vchiq_dump_phys_mem
1384 *
1385 * Arguments: const VCHI_SERVICE_HANDLE_T handle
1386 * void *buffer
1387 * size_t num_bytes
1388 *
1389 * Description: Dumps the physical memory associated with
1390 * a buffer.
1391 *
1392 * Returns: void
1393 *
1394 ***********************************************************/
1395VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T handle,
1396 void *ptr,
1397 size_t num_bytes )
1398{
1399 VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle;
1400 VCHIQ_DUMP_MEM_T dump_mem;
1401 int ret;
1402
1403 if (!service)
1404 return VCHIQ_ERROR;
1405
1406 dump_mem.virt_addr = ptr;
1407 dump_mem.num_bytes = num_bytes;
1408
1409 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_DUMP_PHYS_MEM, &dump_mem));
1410 return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR;
1411}
1412
1413
1414
1415/*
1416 * Support functions
1417 */
1418
1419static VCHIQ_INSTANCE_T
1420vchiq_lib_init(const int dev_vchiq_fd)
1421{
1422 static int mutex_initialised = 0;
1423 static VCOS_MUTEX_T vchiq_lib_mutex;
1424 VCHIQ_INSTANCE_T instance = &vchiq_instance;
1425
1426 vcos_global_lock();
1427 if (!mutex_initialised)
1428 {
1429 vcos_mutex_create(&vchiq_lib_mutex, "vchiq-init");
1430
1431 vcos_log_set_level( &vchiq_lib_log_category, vchiq_default_lib_log_level );
1432 vcos_log_register( "vchiq_lib", &vchiq_lib_log_category );
1433
1434 mutex_initialised = 1;
1435 }
1436 vcos_global_unlock();
1437
1438 vcos_mutex_lock(&vchiq_lib_mutex);
1439
1440 if (instance->initialised == 0)
1441 {
1442 instance->fd = dev_vchiq_fd == -1 ?
1443 open("/dev/vchiq", O_RDWR) :
1444 dup(dev_vchiq_fd);
1445 if (instance->fd >= 0)
1446 {
1447 VCHIQ_GET_CONFIG_T args;
1448 VCHIQ_CONFIG_T config;
1449 int ret;
1450 args.config_size = sizeof(config);
1451 args.pconfig = &config;
1452 RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args));
1453 if ((ret == 0) && (config.version >= VCHIQ_VERSION_MIN) && (config.version_min <= VCHIQ_VERSION))
1454 {
1455 if (config.version >= VCHIQ_VERSION_LIB_VERSION)
1456 {
1457 RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_LIB_VERSION, VCHIQ_VERSION));
1458 }
1459 if (ret == 0)
1460 {
1461 instance->used_services = 0;
1462 instance->use_close_delivered = (config.version >= VCHIQ_VERSION_CLOSE_DELIVERED);
1463 vcos_mutex_create(&instance->mutex, "VCHIQ instance");
1464 instance->initialised = 1;
1465 }
1466 }
1467 else
1468 {
1469 if (ret == 0)
1470 {
1471 vcos_log_error("Incompatible VCHIQ library - driver version %d (min %d), library version %d (min %d)",
1472 config.version, config.version_min, VCHIQ_VERSION, VCHIQ_VERSION_MIN);
1473 }
1474 else
1475 {
1476 vcos_log_error("Very incompatible VCHIQ library - cannot retrieve driver version");
1477 }
1478 close(instance->fd);
1479 instance = NULL;
1480 }
1481 }
1482 else
1483 {
1484 instance = NULL;
1485 }
1486 }
1487 else if (instance->initialised > 0)
1488 {
1489 instance->initialised++;
1490 }
1491
1492 vcos_mutex_unlock(&vchiq_lib_mutex);
1493
1494 return instance;
1495}
1496
1497static void *
1498completion_thread(void *arg)
1499{
1500 VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)arg;
1501 VCHIQ_AWAIT_COMPLETION_T args;
1502 VCHIQ_COMPLETION_DATA_T completions[8];
1503 void *msgbufs[8];
1504
1505 static const VCHI_CALLBACK_REASON_T vchiq_reason_to_vchi[] =
1506 {
1507 VCHI_CALLBACK_SERVICE_OPENED, // VCHIQ_SERVICE_OPENED
1508 VCHI_CALLBACK_SERVICE_CLOSED, // VCHIQ_SERVICE_CLOSED
1509 VCHI_CALLBACK_MSG_AVAILABLE, // VCHIQ_MESSAGE_AVAILABLE
1510 VCHI_CALLBACK_BULK_SENT, // VCHIQ_BULK_TRANSMIT_DONE
1511 VCHI_CALLBACK_BULK_RECEIVED, // VCHIQ_BULK_RECEIVE_DONE
1512 VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, // VCHIQ_BULK_TRANSMIT_ABORTED
1513 VCHI_CALLBACK_BULK_RECEIVE_ABORTED, // VCHIQ_BULK_RECEIVE_ABORTED
1514 };
1515
1516 args.count = vcos_countof(completions);
1517 args.buf = completions;
1518 args.msgbufsize = MSGBUF_SIZE;
1519 args.msgbufcount = 0;
1520 args.msgbufs = msgbufs;
1521
1522 while (1)
1523 {
1524 int count, i;
1525
1526 while ((unsigned int)args.msgbufcount < vcos_countof(msgbufs))
1527 {
1528 void *msgbuf = alloc_msgbuf();
1529 if (msgbuf)
1530 {
1531 msgbufs[args.msgbufcount++] = msgbuf;
1532 }
1533 else
1534 {
1535 vcos_log_error("vchiq_lib: failed to allocate a message buffer\n");
1536 vcos_demand(args.msgbufcount != 0);
1537 }
1538 }
1539
1540 RETRY(count, ioctl(instance->fd, VCHIQ_IOC_AWAIT_COMPLETION, &args));
1541
1542 if (count <= 0)
1543 break;
1544
1545 for (i = 0; i < count; i++)
1546 {
1547 VCHIQ_COMPLETION_DATA_T *completion = &completions[i];
1548 VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)completion->service_userdata;
1549 if (service->base.callback)
1550 {
1551 vcos_log_trace( "callback(%x, %x, %x(%x,%x), %x)",
1552 completion->reason, (uint32_t)completion->header,
1553 (uint32_t)&service->base, (uint32_t)service->lib_handle, (uint32_t)service->base.userdata, (uint32_t)completion->bulk_userdata );
1554 service->base.callback(completion->reason, completion->header,
1555 service->lib_handle, completion->bulk_userdata);
1556 }
1557 else if (service->vchi_callback)
1558 {
1559 VCHI_CALLBACK_REASON_T vchi_reason =
1560 vchiq_reason_to_vchi[completion->reason];
1561 service->vchi_callback(service->base.userdata, vchi_reason, completion->bulk_userdata);
1562 }
1563
1564 if ((completion->reason == VCHIQ_SERVICE_CLOSED) &&
1565 instance->use_close_delivered)
1566 {
1567 int ret;
1568 RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_DELIVERED, service->handle));
1569 }
1570 }
1571 }
1572
1573 while (args.msgbufcount)
1574 {
1575 void *msgbuf = msgbufs[--args.msgbufcount];
1576 free_msgbuf(msgbuf);
1577 }
1578
1579 return NULL;
1580}
1581
1582static VCHIQ_STATUS_T
1583create_service(VCHIQ_INSTANCE_T instance,
1584 const VCHIQ_SERVICE_PARAMS_T *params,
1585 VCHI_CALLBACK_T vchi_callback,
1586 int is_open,
1587 VCHIQ_SERVICE_HANDLE_T *phandle)
1588{
1589 VCHIQ_SERVICE_T *service = NULL;
1590 VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
1591 int i;
1592
1593 if (!is_valid_instance(instance))
1594 return VCHIQ_ERROR;
1595
1596 vcos_mutex_lock(&instance->mutex);
1597
1598 /* Find a free service */
1599 if (is_open)
1600 {
1601 /* Find a free service */
1602 for (i = 0; i < instance->used_services; i++)
1603 {
1604 if (instance->services[i].lib_handle == VCHIQ_SERVICE_HANDLE_INVALID)
1605 {
1606 service = &instance->services[i];
1607 break;
1608 }
1609 }
1610 }
1611 else
1612 {
1613 for (i = (instance->used_services - 1); i >= 0; i--)
1614 {
1615 VCHIQ_SERVICE_T *srv = &instance->services[i];
1616 if (srv->lib_handle == VCHIQ_SERVICE_HANDLE_INVALID)
1617 {
1618 service = srv;
1619 }
1620 else if (
1621 (srv->base.fourcc == params->fourcc) &&
1622 ((srv->base.callback != params->callback) ||
1623 (srv->vchi_callback != vchi_callback)))
1624 {
1625 /* There is another server using this fourcc which doesn't match */
1626 vcos_log_info("service %x already using fourcc 0x%x",
1627 srv->lib_handle, params->fourcc);
1628 service = NULL;
1629 status = VCHIQ_ERROR;
1630 break;
1631 }
1632 }
1633 }
1634
1635 if (!service && (status == VCHIQ_SUCCESS))
1636 {
1637 if (instance->used_services < VCHIQ_MAX_INSTANCE_SERVICES)
1638 service = &instance->services[instance->used_services++];
1639 else
1640 status = VCHIQ_ERROR;
1641 }
1642
1643 if (service)
1644 {
1645 if (!handle_seq)
1646 handle_seq = VCHIQ_MAX_INSTANCE_SERVICES;
1647 service->lib_handle = handle_seq | (service - instance->services);
1648 handle_seq += VCHIQ_MAX_INSTANCE_SERVICES;
1649 }
1650
1651 vcos_mutex_unlock(&instance->mutex);
1652
1653 if (service)
1654 {
1655 VCHIQ_CREATE_SERVICE_T args;
1656 int ret;
1657
1658 service->base.fourcc = params->fourcc;
1659 service->base.callback = params->callback;
1660 service->vchi_callback = vchi_callback;
1661 service->base.userdata = params->userdata;
1662 service->fd = instance->fd;
1663 service->peek_size = -1;
1664 service->peek_buf = NULL;
1665 service->is_client = is_open;
1666
1667 args.params = *params;
1668 args.params.userdata = service;
1669 args.is_open = is_open;
1670 args.is_vchi = (params->callback == NULL);
1671 args.handle = VCHIQ_SERVICE_HANDLE_INVALID; /* OUT parameter */
1672 RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_CREATE_SERVICE, &args));
1673 if (ret == 0)
1674 service->handle = args.handle;
1675 else
1676 status = VCHIQ_ERROR;
1677 }
1678
1679 if (status == VCHIQ_SUCCESS)
1680 {
1681 *phandle = service->lib_handle;
1682 vcos_log_info("service handle %x lib_handle %x using fourcc 0x%x",
1683 service->handle, service->lib_handle, params->fourcc);
1684 }
1685 else
1686 {
1687 vcos_mutex_lock(&instance->mutex);
1688
1689 if (service)
1690 service->lib_handle = VCHIQ_SERVICE_HANDLE_INVALID;
1691
1692 vcos_mutex_unlock(&instance->mutex);
1693
1694 *phandle = VCHIQ_SERVICE_HANDLE_INVALID;
1695 }
1696
1697 return status;
1698}
1699
1700static int
1701fill_peek_buf(VCHI_SERVICE_T *service,
1702 VCHI_FLAGS_T flags)
1703{
1704 VCHIQ_DEQUEUE_MESSAGE_T args;
1705 int ret = 0;
1706
1707 vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
1708
1709 if (service->peek_size < 0)
1710 {
1711 if (!service->peek_buf)
1712 service->peek_buf = alloc_msgbuf();
1713
1714 if (service->peek_buf)
1715 {
1716 args.handle = service->handle;
1717 args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE);
1718 args.bufsize = MSGBUF_SIZE;
1719 args.buf = service->peek_buf;
1720
1721 RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args));
1722
1723 if (ret >= 0)
1724 {
1725 service->peek_size = ret;
1726 ret = 0;
1727 }
1728 else
1729 {
1730 ret = -1;
1731 }
1732 }
1733 else
1734 {
1735 ret = -1;
1736 }
1737 }
1738
1739 return ret;
1740}
1741
1742
1743static void *
1744alloc_msgbuf(void)
1745{
1746 void *msgbuf;
1747 vcos_mutex_lock(&vchiq_lib_mutex);
1748 msgbuf = free_msgbufs;
1749 if (msgbuf)
1750 free_msgbufs = *(void **)msgbuf;
1751 vcos_mutex_unlock(&vchiq_lib_mutex);
1752 if (!msgbuf)
1753 msgbuf = vcos_malloc(MSGBUF_SIZE, "alloc_msgbuf");
1754 return msgbuf;
1755}
1756
1757static void
1758free_msgbuf(void *buf)
1759{
1760 vcos_mutex_lock(&vchiq_lib_mutex);
1761 *(void **)buf = free_msgbufs;
1762 free_msgbufs = buf;
1763 vcos_mutex_unlock(&vchiq_lib_mutex);
1764}
1765