1 | /* |
2 | Copyright (c) 2012-2014, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 | |
48 | typedef 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 | |
61 | typedef struct vchiq_service_struct VCHI_SERVICE_T; |
62 | |
63 | struct 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 | |
75 | typedef struct vchiq_instance_struct VCHI_STATE_T; |
76 | |
77 | /* Local data */ |
78 | static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN; |
79 | static VCOS_LOG_CAT_T vchiq_lib_log_category; |
80 | static VCOS_MUTEX_T vchiq_lib_mutex; |
81 | static void *free_msgbufs; |
82 | static unsigned int handle_seq; |
83 | |
84 | vcos_static_assert(IS_POWER_2(VCHIQ_MAX_INSTANCE_SERVICES)); |
85 | |
86 | /* Local utility functions */ |
87 | static VCHIQ_INSTANCE_T |
88 | vchiq_lib_init(const int dev_vchiq_fd); |
89 | |
90 | static void *completion_thread(void *); |
91 | |
92 | static VCHIQ_STATUS_T |
93 | create_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 | |
99 | static int |
100 | fill_peek_buf(VCHI_SERVICE_T *service, |
101 | VCHI_FLAGS_T flags); |
102 | |
103 | static void * |
104 | alloc_msgbuf(void); |
105 | |
106 | static void |
107 | free_msgbuf(void *buf); |
108 | |
109 | static __inline int |
110 | is_valid_instance(VCHIQ_INSTANCE_T instance) |
111 | { |
112 | return (instance == &vchiq_instance) && (instance->initialised > 0); |
113 | } |
114 | |
115 | static inline VCHIQ_SERVICE_T * |
116 | handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) |
117 | { |
118 | return &vchiq_instance.services[handle & (VCHIQ_MAX_INSTANCE_SERVICES - 1)]; |
119 | } |
120 | |
121 | static VCHIQ_SERVICE_T * |
122 | find_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. |
146 | VCHIQ_STATUS_T |
147 | vchiq_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 | |
160 | VCHIQ_STATUS_T |
161 | vchiq_initialise(VCHIQ_INSTANCE_T *pinstance) |
162 | { |
163 | return vchiq_initialise_fd(pinstance, -1); |
164 | } |
165 | |
166 | VCHIQ_STATUS_T |
167 | vchiq_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 | |
229 | VCHIQ_STATUS_T |
230 | vchiq_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 | |
263 | out: |
264 | vcos_mutex_unlock(&instance->mutex); |
265 | return status; |
266 | } |
267 | |
268 | VCHIQ_STATUS_T |
269 | vchiq_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 | |
300 | VCHIQ_STATUS_T |
301 | vchiq_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 | |
332 | VCHIQ_STATUS_T |
333 | vchiq_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 | |
354 | VCHIQ_STATUS_T |
355 | vchiq_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 | |
375 | VCHIQ_STATUS_T |
376 | vchiq_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 | |
397 | void |
398 | vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, |
399 | VCHIQ_HEADER_T *) |
400 | { |
401 | vcos_log_trace( "%s handle=%08x, header=%x" , __func__, (uint32_t)handle, (uint32_t)header ); |
402 | |
403 | free_msgbuf(header); |
404 | } |
405 | |
406 | VCHIQ_STATUS_T |
407 | vchiq_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 | |
431 | VCHIQ_STATUS_T |
432 | vchiq_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 | |
456 | VCHIQ_STATUS_T |
457 | vchiq_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 | |
470 | VCHIQ_STATUS_T |
471 | vchiq_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 | |
484 | VCHIQ_STATUS_T |
485 | vchiq_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 | |
510 | VCHIQ_STATUS_T |
511 | vchiq_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 | |
520 | VCHIQ_STATUS_T |
521 | vchiq_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 | |
533 | VCHIQ_STATUS_T |
534 | vchiq_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 | |
563 | int |
564 | vchiq_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 | |
574 | void * |
575 | vchiq_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 | |
582 | int |
583 | vchiq_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 | |
590 | VCHIQ_STATUS_T |
591 | vchiq_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 | |
609 | int32_t |
610 | vchiq_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 | |
622 | int32_t |
623 | vchiq_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 | |
635 | VCHIQ_STATUS_T |
636 | vchiq_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 | * -------------------------------------------------------------------- */ |
662 | const VCHI_MESSAGE_DRIVER_T * |
663 | vchi_mphi_message_driver_func_table( void ) |
664 | { |
665 | return NULL; |
666 | } |
667 | |
668 | /* ---------------------------------------------------------------------- |
669 | * return a pointer to the 'single' connection driver fops |
670 | * -------------------------------------------------------------------- */ |
671 | const VCHI_CONNECTION_API_T * |
672 | single_get_func_table( void ) |
673 | { |
674 | return NULL; |
675 | } |
676 | |
677 | VCHI_CONNECTION_T * |
678 | vchi_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 | ***********************************************************/ |
701 | int32_t |
702 | vchi_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 | ***********************************************************/ |
734 | int32_t |
735 | vchi_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 | ***********************************************************/ |
765 | int32_t |
766 | vchi_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 | ***********************************************************/ |
805 | int32_t |
806 | vchi_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 | ***********************************************************/ |
858 | int32_t |
859 | vchi_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 | ***********************************************************/ |
912 | int32_t |
913 | vchi_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 | |
979 | vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); |
980 | vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); |
981 | vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); |
982 | |
983 | int32_t |
984 | vchi_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 | ***********************************************************/ |
1019 | int32_t |
1020 | vchi_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 | ***********************************************************/ |
1049 | int32_t |
1050 | vchi_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 | ***********************************************************/ |
1093 | int32_t |
1094 | vchi_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 | ***********************************************************/ |
1120 | int32_t |
1121 | vchi_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 | ***********************************************************/ |
1147 | int32_t |
1148 | vchi_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 | ***********************************************************/ |
1171 | int32_t |
1172 | vchi_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(¶ms, 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 | ¶ms, |
1187 | setup->callback, |
1188 | 1/*open*/, |
1189 | (VCHIQ_SERVICE_HANDLE_T *)handle); |
1190 | |
1191 | return (status == VCHIQ_SUCCESS) ? 0 : -1; |
1192 | } |
1193 | |
1194 | int32_t |
1195 | vchi_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(¶ms, 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 | ¶ms, |
1209 | setup->callback, |
1210 | 0/*!open*/, |
1211 | (VCHIQ_SERVICE_HANDLE_T *)handle); |
1212 | |
1213 | return (status == VCHIQ_SUCCESS) ? 0 : -1; |
1214 | } |
1215 | |
1216 | int32_t |
1217 | vchi_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 | |
1233 | int32_t |
1234 | vchi_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 | * -------------------------------------------------------------------- */ |
1253 | uint32_t |
1254 | vchi_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 | * -------------------------------------------------------------------- */ |
1264 | void |
1265 | vchi_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 | * -------------------------------------------------------------------- */ |
1278 | uint16_t |
1279 | vchi_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 | * -------------------------------------------------------------------- */ |
1289 | void |
1290 | vchi_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 | ***********************************************************/ |
1307 | int32_t |
1308 | vchi_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 | ***********************************************************/ |
1330 | int32_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 | ***********************************************************/ |
1354 | int32_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 | ***********************************************************/ |
1395 | VCHIQ_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 | |
1419 | static VCHIQ_INSTANCE_T |
1420 | vchiq_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 | |
1497 | static void * |
1498 | completion_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 | |
1582 | static VCHIQ_STATUS_T |
1583 | create_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 | |
1700 | static int |
1701 | fill_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 | |
1743 | static void * |
1744 | alloc_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 | |
1757 | static void |
1758 | free_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 | |