1/*
2Copyright (c) 2012, 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#define VCOS_LOG_CATEGORY (&khrn_client_log)
28
29#include "vchiq.h"
30#include "interface/khronos/common/khrn_int_common.h"
31#include "interface/khronos/common/khrn_int_ids.h"
32
33#include "interface/khronos/common/khrn_client.h"
34#include "interface/khronos/common/khrn_client_rpc.h"
35
36
37#include <string.h>
38#include <stdio.h>
39
40extern VCOS_LOG_CAT_T khrn_client_log;
41
42static void *workspace; /* for scatter/gather bulks */
43static PLATFORM_MUTEX_T mutex;
44
45#define FOURCC_KHAN VCHIQ_MAKE_FOURCC('K', 'H', 'A', 'N')
46#define FOURCC_KHRN VCHIQ_MAKE_FOURCC('K', 'H', 'R', 'N')
47#define FOURCC_KHHN VCHIQ_MAKE_FOURCC('K', 'H', 'H', 'N')
48
49static VCHIQ_INSTANCE_T khrn_vchiq_instance;
50
51static VCHIQ_SERVICE_HANDLE_T vchiq_khan_service;
52static VCHIQ_SERVICE_HANDLE_T vchiq_khrn_service;
53static VCHIQ_SERVICE_HANDLE_T vchiq_khhn_service;
54
55static VCHIU_QUEUE_T khrn_queue;
56static VCHIU_QUEUE_T khhn_queue;
57
58static VCOS_EVENT_T bulk_event;
59
60VCHIQ_STATUS_T khrn_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
61 VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)
62{
63 switch (reason) {
64 case VCHIQ_MESSAGE_AVAILABLE:
65 vchiu_queue_push(&khrn_queue, header);
66 break;
67 case VCHIQ_BULK_TRANSMIT_DONE:
68 case VCHIQ_BULK_RECEIVE_DONE:
69 vcos_event_signal(&bulk_event);
70 break;
71 case VCHIQ_SERVICE_OPENED:
72 case VCHIQ_SERVICE_CLOSED:
73 case VCHIQ_BULK_TRANSMIT_ABORTED:
74 case VCHIQ_BULK_RECEIVE_ABORTED:
75 UNREACHABLE(); /* not implemented */
76 }
77
78 return VCHIQ_SUCCESS;
79}
80
81VCHIQ_STATUS_T khhn_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
82 VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)
83{
84 switch (reason) {
85 case VCHIQ_MESSAGE_AVAILABLE:
86 vchiu_queue_push(&khhn_queue, header);
87 break;
88 case VCHIQ_BULK_TRANSMIT_DONE:
89 case VCHIQ_BULK_RECEIVE_DONE:
90 vcos_event_signal(&bulk_event);
91 break;
92 case VCHIQ_SERVICE_OPENED:
93 case VCHIQ_SERVICE_CLOSED:
94 case VCHIQ_BULK_TRANSMIT_ABORTED:
95 case VCHIQ_BULK_RECEIVE_ABORTED:
96 UNREACHABLE(); /* not implemented */
97 }
98
99 return VCHIQ_SUCCESS;
100}
101
102VCHIQ_STATUS_T khan_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
103 VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata)
104{
105 switch (reason) {
106 case VCHIQ_MESSAGE_AVAILABLE:
107 {
108 int32_t *data = (int32_t *) header->data;
109 int32_t command = data[0];
110 int32_t *msg = &data[1];
111 vcos_assert(header->size == 16);
112
113 // TODO should be able to remove this eventually.
114 // If incoming message is not addressed to this process, then ignore it.
115 // Correct process should then pick it up.
116 uint64_t pid = vchiq_get_client_id(handle);
117 if((msg[0] != (uint32_t) pid) || (msg[1] != (uint32_t) (pid >> 32)))
118 {
119 printf("khan_callback: message for wrong process; pid = %X, msg pid = %X\n",
120 (uint32_t) pid, msg[0]);
121 return VCHIQ_SUCCESS;
122 } // if
123
124 if (command == ASYNC_COMMAND_DESTROY)
125 {
126 /* todo: destroy */
127 }
128 else
129 {
130 PLATFORM_SEMAPHORE_T sem;
131 if (khronos_platform_semaphore_create(&sem, msg, 1) == KHR_SUCCESS)
132 {
133 switch (command) {
134 case ASYNC_COMMAND_WAIT:
135 khronos_platform_semaphore_acquire(&sem);
136 break;
137 case ASYNC_COMMAND_POST:
138 khronos_platform_semaphore_release(&sem);
139 break;
140 default:
141 vcos_assert_msg(0, "khan_callback: unknown message type");
142 break;
143 }
144 khronos_platform_semaphore_destroy(&sem);
145 }
146 } // else
147 vchiq_release_message(handle, header);
148 break;
149 }
150 case VCHIQ_BULK_TRANSMIT_DONE:
151 case VCHIQ_BULK_RECEIVE_DONE:
152 UNREACHABLE();
153 break;
154 case VCHIQ_SERVICE_OPENED:
155 vcos_assert_msg(0, "khan_callback: VCHIQ_SERVICE_OPENED");
156 break;
157 case VCHIQ_SERVICE_CLOSED:
158 vcos_assert_msg(0, "khan_callback: VCHIQ_SERVICE_CLOSED");
159 break;
160 case VCHIQ_BULK_TRANSMIT_ABORTED:
161 case VCHIQ_BULK_RECEIVE_ABORTED:
162 default:
163 UNREACHABLE(); /* not implemented */
164 }
165
166 return VCHIQ_SUCCESS;
167}
168
169void vc_vchi_khronos_init()
170{
171 VCOS_STATUS_T status = vcos_event_create(&bulk_event, NULL);
172 UNUSED_NDEBUG(status);
173 vcos_assert(status == VCOS_SUCCESS);
174
175 if (vchiq_initialise(&khrn_vchiq_instance) != VCHIQ_SUCCESS)
176 {
177 vcos_log_error("* failed to open vchiq device");
178
179 exit(1);
180 }
181
182 vcos_log_trace("gldemo: connecting");
183
184 if (vchiq_connect(khrn_vchiq_instance) != VCHIQ_SUCCESS)
185 {
186 vcos_log_error("* failed to connect");
187
188 exit(1);
189 }
190
191 VCHIQ_STATUS_T khan_return, khrn_return, khhn_return;
192 VCHIQ_SERVICE_PARAMS_T params;
193
194 params.userdata = NULL;
195 params.version = VC_KHRN_VERSION;
196 params.version_min = VC_KHRN_VERSION;
197
198 params.fourcc = FOURCC_KHAN;
199 params.callback = khan_callback;
200 khan_return = vchiq_open_service(khrn_vchiq_instance, &params, &vchiq_khan_service);
201
202 params.fourcc = FOURCC_KHRN;
203 params.callback = khrn_callback;
204 khrn_return = vchiq_open_service(khrn_vchiq_instance, &params, &vchiq_khrn_service);
205
206 params.fourcc = FOURCC_KHHN;
207 params.callback = khhn_callback;
208 khhn_return = vchiq_open_service(khrn_vchiq_instance, &params, &vchiq_khhn_service);
209
210 if (khan_return != VCHIQ_SUCCESS ||
211 khrn_return != VCHIQ_SUCCESS ||
212 khhn_return != VCHIQ_SUCCESS)
213 {
214 vcos_log_error("* failed to add service - already in use?");
215
216 exit(1);
217 }
218 vchiu_queue_init(&khrn_queue, 64);
219 vchiu_queue_init(&khhn_queue, 64);
220
221 vcos_log_trace("gldemo: connected");
222
223 /*
224 attach to process (there's just one)
225 */
226
227// bool success = client_process_attach();
228// vcos_assert(success);
229}
230
231bool khclient_rpc_init(void)
232{
233 workspace = NULL;
234 return platform_mutex_create(&mutex) == KHR_SUCCESS;
235}
236
237void rpc_term(void)
238{
239 if (workspace) { khrn_platform_free(workspace); }
240 platform_mutex_destroy(&mutex);
241}
242
243static VCHIQ_SERVICE_HANDLE_T get_handle(CLIENT_THREAD_STATE_T *thread)
244{
245 VCHIQ_SERVICE_HANDLE_T result = (thread->high_priority ? vchiq_khhn_service : vchiq_khrn_service);
246 return result;
247}
248
249static VCHIU_QUEUE_T *get_queue(CLIENT_THREAD_STATE_T *thread)
250{
251 return thread->high_priority ? &khhn_queue : &khrn_queue;
252}
253
254static void check_workspace(uint32_t size)
255{
256 /* todo: find a better way to handle scatter/gather bulks */
257 vcos_assert(size <= KHDISPATCH_WORKSPACE_SIZE);
258 if (!workspace) {
259 workspace = khrn_platform_malloc(KHDISPATCH_WORKSPACE_SIZE, "rpc_workspace");
260 vcos_assert(workspace);
261 }
262}
263
264static void merge_flush(CLIENT_THREAD_STATE_T *thread)
265{
266 vcos_log_trace("merge_flush start");
267
268 vcos_assert(thread->merge_pos >= CLIENT_MAKE_CURRENT_SIZE);
269
270 /*
271 don't transmit just a make current -- in the case that there is only a
272 make current in the merge buffer, we have already sent a control message
273 for the rpc call (and with it a make current) and own the big lock
274 */
275
276 if (thread->merge_pos > CLIENT_MAKE_CURRENT_SIZE) {
277 VCHIQ_ELEMENT_T element;
278
279 rpc_begin(thread);
280
281 element.data = thread->merge_buffer;
282 element.size = thread->merge_pos;
283
284 VCHIQ_STATUS_T success = vchiq_queue_message(get_handle(thread), &element, 1);
285 UNUSED_NDEBUG(success);
286 vcos_assert(success == VCHIQ_SUCCESS);
287
288 thread->merge_pos = 0;
289
290 client_send_make_current(thread);
291
292 vcos_assert(thread->merge_pos == CLIENT_MAKE_CURRENT_SIZE);
293
294 rpc_end(thread);
295 }
296 vcos_log_trace( "merge_flush end");
297
298}
299
300void rpc_flush(CLIENT_THREAD_STATE_T *thread)
301{
302 merge_flush(thread);
303}
304
305void rpc_high_priority_begin(CLIENT_THREAD_STATE_T *thread)
306{
307 vcos_assert(!thread->high_priority);
308 merge_flush(thread);
309 thread->high_priority = true;
310}
311
312void rpc_high_priority_end(CLIENT_THREAD_STATE_T *thread)
313{
314 vcos_assert(thread->high_priority);
315 merge_flush(thread);
316 thread->high_priority = false;
317}
318
319uint32_t rpc_send_ctrl_longest(CLIENT_THREAD_STATE_T *thread, uint32_t len_min)
320{
321 uint32_t len = MERGE_BUFFER_SIZE - thread->merge_pos;
322 if (len < len_min) {
323 len = MERGE_BUFFER_SIZE - CLIENT_MAKE_CURRENT_SIZE;
324 }
325 return len;
326}
327
328void rpc_send_ctrl_begin(CLIENT_THREAD_STATE_T *thread, uint32_t len)
329{
330 //CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
331
332 vcos_assert(len == rpc_pad_ctrl(len));
333 if ((thread->merge_pos + len) > MERGE_BUFFER_SIZE) {
334 merge_flush(thread);
335 }
336
337 thread->merge_end = thread->merge_pos + len;
338}
339
340void rpc_send_ctrl_write(CLIENT_THREAD_STATE_T *thread, const uint32_t in[], uint32_t len) /* len bytes read, rpc_pad_ctrl(len) bytes written */
341{
342 //CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
343
344 memcpy(thread->merge_buffer + thread->merge_pos, in, len);
345 thread->merge_pos += rpc_pad_ctrl(len);
346 vcos_assert(thread->merge_pos <= MERGE_BUFFER_SIZE);
347}
348
349void rpc_send_ctrl_end(CLIENT_THREAD_STATE_T *thread)
350{
351 //CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
352
353 vcos_assert(thread->merge_pos == thread->merge_end);
354}
355
356static void send_bulk(CLIENT_THREAD_STATE_T *thread, const void *in, uint32_t len)
357{
358 if (len <= KHDISPATCH_CTRL_THRESHOLD) {
359 VCHIQ_ELEMENT_T element;
360
361 element.data = in;
362 element.size = len;
363
364 VCHIQ_STATUS_T vchiq_status = vchiq_queue_message(get_handle(thread), &element, 1);
365 UNUSED_NDEBUG(vchiq_status);
366 vcos_assert(vchiq_status == VCHIQ_SUCCESS);
367 } else {
368 VCHIQ_STATUS_T vchiq_status = vchiq_queue_bulk_transmit(get_handle(thread), in, rpc_pad_bulk(len), NULL);
369 UNUSED_NDEBUG(vchiq_status);
370 vcos_assert(vchiq_status == VCHIQ_SUCCESS);
371 VCOS_STATUS_T vcos_status = vcos_event_wait(&bulk_event);
372 UNUSED_NDEBUG(vcos_status);
373 vcos_assert(vcos_status == VCOS_SUCCESS);
374 }
375}
376
377static void recv_bulk(CLIENT_THREAD_STATE_T *thread, void *out, uint32_t len)
378{
379 if (len <= KHDISPATCH_CTRL_THRESHOLD) {
380 VCHIQ_HEADER_T *header = vchiu_queue_pop(get_queue(thread));
381 vcos_assert(header->size == len);
382 memcpy(out, header->data, len);
383 vchiq_release_message(get_handle(thread), header);
384 } else {
385 VCHIQ_STATUS_T vchiq_status = vchiq_queue_bulk_receive(get_handle(thread), out, rpc_pad_bulk(len), NULL);
386 UNUSED_NDEBUG(vchiq_status);
387 vcos_assert(vchiq_status == VCHIQ_SUCCESS);
388 VCOS_STATUS_T vcos_status = vcos_event_wait(&bulk_event);
389 UNUSED_NDEBUG(vcos_status);
390 vcos_assert(vcos_status == VCOS_SUCCESS);
391 }
392}
393
394void rpc_send_bulk(CLIENT_THREAD_STATE_T *thread, const void *in, uint32_t len)
395{
396 if (in && len) {
397 //CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
398
399 merge_flush(thread);
400
401 send_bulk(thread, in, len);
402 }
403}
404
405void rpc_send_bulk_gather(CLIENT_THREAD_STATE_T *thread, const void *in, uint32_t len, int32_t stride, uint32_t n)
406{
407#if 1
408 if (in && len) {
409 //CLIENT_THREAD_STATE_T *thread = CLIENT_GET_THREAD_STATE();
410
411 merge_flush(thread);
412
413 if (len == stride) {
414 /* hopefully should be the common case */
415 send_bulk(thread, in, n * len);
416 } else {
417 check_workspace(n * len);
418 rpc_gather(workspace, in, len, stride, n);
419 send_bulk(thread, workspace, n * len);
420 }
421 }
422#else
423 UNREACHABLE();
424#endif
425}
426
427uint32_t rpc_recv(CLIENT_THREAD_STATE_T *thread, void *out, uint32_t *len_io, RPC_RECV_FLAG_T flags)
428{
429 uint32_t res = 0;
430 uint32_t len;
431 bool recv_ctrl;
432
433 if (!len_io) { len_io = &len; }
434
435 recv_ctrl = flags & (RPC_RECV_FLAG_RES | RPC_RECV_FLAG_CTRL | RPC_RECV_FLAG_LEN); /* do we want to receive anything in the control channel at all? */
436 vcos_assert(recv_ctrl || (flags & RPC_RECV_FLAG_BULK)); /* must receive something... */
437 vcos_assert(!(flags & RPC_RECV_FLAG_CTRL) || !(flags & RPC_RECV_FLAG_BULK)); /* can't receive user data over both bulk and control... */
438
439 if (recv_ctrl || len_io[0]) { /* do nothing if we're just receiving bulk of length 0 */
440 merge_flush(thread);
441
442 if (recv_ctrl) {
443 VCHIQ_HEADER_T *header = vchiu_queue_pop(get_queue(thread));
444 uint8_t *ctrl = (uint8_t *)header->data;
445 vcos_assert(header->size >= (!!(flags & RPC_RECV_FLAG_LEN)*4 + !!(flags & RPC_RECV_FLAG_RES)*4) );
446 if (flags & RPC_RECV_FLAG_LEN) {
447 len_io[0] = *((uint32_t *)ctrl);
448 ctrl += 4;
449 }
450 if (flags & RPC_RECV_FLAG_RES) {
451 res = *((uint32_t *)ctrl);
452 ctrl += 4;
453 }
454 if (flags & RPC_RECV_FLAG_CTRL) {
455 memcpy(out, ctrl, len_io[0]);
456 ctrl += len_io[0];
457 }
458 vcos_assert(ctrl == ((uint8_t *)header->data + header->size));
459 vchiq_release_message(get_handle(thread), header);
460 }
461
462 if ((flags & RPC_RECV_FLAG_BULK) && len_io[0]) {
463 if (flags & RPC_RECV_FLAG_BULK_SCATTER) {
464 if ((len_io[0] == len_io[1]) && !len_io[3] && !len_io[4]) {
465 /* hopefully should be the common case */
466 recv_bulk(thread, out, len_io[2] * len_io[0]);
467 } else {
468 check_workspace(len_io[2] * len_io[0]);
469 recv_bulk(thread, workspace, len_io[2] * len_io[0]);
470 rpc_scatter(out, len_io[0], len_io[1], len_io[2], len_io[3], len_io[4], workspace);
471 }
472 } else {
473 recv_bulk(thread, out, len_io[0]);
474 }
475 }
476 }
477
478 return res;
479}
480
481void rpc_begin(CLIENT_THREAD_STATE_T *thread)
482{
483 UNUSED(thread);
484 platform_mutex_acquire(&mutex);
485}
486
487void rpc_end(CLIENT_THREAD_STATE_T *thread)
488{
489 UNUSED(thread);
490 platform_mutex_release(&mutex);
491}
492
493void rpc_use(CLIENT_THREAD_STATE_T *thread)
494{ // TODO - implement this for linux
495 UNUSED(thread);
496}
497
498void rpc_release(CLIENT_THREAD_STATE_T *thread)
499{ // TODO - implement this for linux
500 UNUSED(thread);
501}
502
503void rpc_call8_makecurrent(CLIENT_THREAD_STATE_T *thread, uint32_t id, uint32_t p0,
504 uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4, uint32_t p5, uint32_t p6, uint32_t p7)
505{
506 uint32_t data;
507 if (thread->merge_pos == CLIENT_MAKE_CURRENT_SIZE && (memcpy(&data,thread->merge_buffer,sizeof(data)), data == EGLINTMAKECURRENT_ID))
508 {
509 rpc_begin(thread);
510 vcos_log_trace("rpc_call8_makecurrent collapse onto previous makecurrent");
511
512 thread->merge_pos = 0;
513
514 RPC_CALL8(eglIntMakeCurrent_impl, thread, EGLINTMAKECURRENT_ID, p0, p1, p2, p3, p4, p5, p6, p7);
515 vcos_assert(thread->merge_pos == CLIENT_MAKE_CURRENT_SIZE);
516
517 rpc_end(thread);
518 }
519 else
520 {
521 RPC_CALL8(eglIntMakeCurrent_impl, thread, EGLINTMAKECURRENT_ID, p0, p1, p2, p3, p4, p5, p6, p7);
522 }
523}
524
525uint64_t rpc_get_client_id(CLIENT_THREAD_STATE_T *thread)
526{
527 return vchiq_get_client_id(get_handle(thread));
528}
529