/* Copyright (c) 2012, Broadcom Europe Ltd All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "vchiq_test.h" #ifndef USE_VCHIQ_ARM #define USE_VCHIQ_ARM #endif #include "interface/vchi/vchi.h" #define NUM_BULK_BUFS 2 #define BULK_SIZE (1024*256) #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif #define INIT_PARAMS(sp_, fourcc_, cb_, userdata_, ver_) \ do { \ memset((sp_), 0, sizeof(*(sp_))); \ (sp_)->fourcc = fourcc_; \ (sp_)->callback = cb_; \ (sp_)->userdata = userdata_; \ (sp_)->version = ver_; \ (sp_)->version_min = ver_; \ } while (0) static struct test_params g_params = { MSG_CONFIG, 64, 100, 1, 1, 1, 0, 0, 0, 0 }; static const char *g_servname = "echo"; static VCOS_EVENT_T g_server_reply; static VCOS_EVENT_T g_shutdown; static VCOS_MUTEX_T g_mutex; static const char *g_server_error = NULL; static volatile int g_sync_mode = 0; static VCOS_EVENT_T func_test_sync; static int want_echo = 1; static int func_error = 0; static int fun2_error = 0; static int func_data_test_start = -1; static int func_data_test_end = 0x7fffffff; static int func_data_test_iter; char *bulk_bufs[NUM_BULK_BUFS * 2]; char *bulk_tx_data[NUM_BULK_BUFS]; char *bulk_rx_data[NUM_BULK_BUFS]; static int ctrl_received = 0; static int bulk_tx_sent = 0; static int bulk_rx_sent = 0; static int bulk_tx_received = 0; static int bulk_rx_received = 0; static char clnt_service1_data[SERVICE1_DATA_SIZE]; static char clnt_service2_data[SERVICE2_DATA_SIZE]; static VCOS_LOG_CAT_T vchiq_test_log_category; static int vchiq_test(int argc, char **argv); static VCHIQ_STATUS_T vchiq_bulk_test(void); static VCHIQ_STATUS_T vchiq_ctrl_test(void); static VCHIQ_STATUS_T vchiq_functional_test(void); static VCHIQ_STATUS_T vchiq_ping_test(void); static VCHIQ_STATUS_T vchiq_signal_test(void); static VCHIQ_STATUS_T do_functional_test(void); static void do_ping_test(VCHIQ_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters); static void do_vchi_ping_test(VCHI_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters); static VCHIQ_STATUS_T func_data_test(VCHIQ_SERVICE_HANDLE_T service, int size, int align, int server_align); #ifdef VCHIQ_LOCAL static void *vchiq_test_server(void *); #endif static VCHIQ_STATUS_T clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); static void vchi_clnt_callback(void *callback_param, VCHI_CALLBACK_REASON_T reason, void *handle); static VCHIQ_STATUS_T func_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); static VCHIQ_STATUS_T fun2_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata); static int mem_check(const void *expected, const void *actual, int size); static void usage(void); static void check_timer(void); static char *buf_align(char *buf, int align_size, int align); #ifdef ANDROID static int g_timeout_ms = 0; static pid_t main_process_pid; static void kill_timeout_handler(int cause, siginfo_t *how, void *ucontext); static int setup_auto_kill(int timeout_ms); #endif #ifdef __linux__ #include #include #include "interface/vmcs_host/vc_cma.h" static void reserve_test(int reserve, int delay) { int fd = open("/dev/vc-cma", O_RDWR); int rc = -1; if (fd >= 0) { rc = ioctl(fd, VC_CMA_IOC_RESERVE, reserve); if (rc == 0) { printf("Sleeping for %d seconds...\n", delay); sleep(delay); } else printf("* failed to ioctl /dev/vc-cma - rc %d\n", rc); close(fd); } else printf("* failed to open /dev/vc-cma - rc %d\n", fd); } #endif static int vchiq_test(int argc, char **argv) { VCHIQ_STATUS_T status; int run_bulk_test = 0; int run_ctrl_test = 0; int run_functional_test = 0; int run_ping_test = 0; int run_signal_test = 0; int verbose = 0; int argn; argn = 1; while ((argn < argc) && (argv[argn][0] == '-')) { const char *arg = argv[argn++]; if (strcmp(arg, "-s") == 0) { g_servname = argv[argn++]; if (!g_servname || (strlen(g_servname) != 4)) { usage(); } } else if (strcasecmp(arg, "-a") == 0) { g_params.align_size = (strcmp(arg, "-A") == 0) ? 4096 : 32; g_params.client_align = atoi(argv[argn++]); g_params.server_align = atoi(argv[argn++]); } else if (strcmp(arg, "-b") == 0) { run_bulk_test = 1; g_params.blocksize = atoi(argv[argn++]); } else if (strcmp(arg, "-c") == 0) { run_ctrl_test = 1; g_params.blocksize = atoi(argv[argn++]); } else if (strcmp(arg, "-e") == 0) { want_echo = 0; } else if (strcmp(arg, "-f") == 0) { run_functional_test = 1; } else if (strcmp(arg, "-h") == 0) { usage(); } else if (strcmp(arg, "-i") == 0) { run_signal_test = 1; } else if (strcmp(arg, "-m") == 0) { g_params.client_message_quota = atoi(argv[argn++]); } else if (strcmp(arg, "-M") == 0) { g_params.server_message_quota = atoi(argv[argn++]); } else if (strcmp(arg, "-p") == 0) { run_ping_test = 1; g_params.iters = 1000; } else if (strcmp(arg, "-q") == 0) { /* coverity[missing_lock : FALSE] - g_server_reply is not used for mutual exclusion */ g_params.verify = 0; } #ifdef __linux__ else if (strcmp(arg, "-r") == 0) { int reserve, delay; if (argn+1 < argc) { reserve = atoi(argv[argn++]); delay = atoi(argv[argn++]); reserve_test(reserve, delay); exit(0); } else { printf("not enough arguments (-r reserve delay)\n"); exit(-1); } } #endif #ifdef ANDROID else if (strcmp(arg, "-K") == 0) { if (argn < argc) g_timeout_ms = atoi(argv[argn++]); else { printf("not enough arguments (-K timeout)\n"); exit(-1); } } #endif else if (strcmp(arg, "-t") == 0) { check_timer(); exit(0); } else if (strcmp(arg, "-v") == 0) { verbose = 1; } else if (strcmp(arg, "-S") == 0) { func_data_test_start = atoi(argv[argn++]); } else if (strcmp(arg, "-E") == 0) { func_data_test_end = atoi(argv[argn++]); } else { printf("* unknown option '%s'\n", arg); usage(); } } if ((run_ctrl_test + run_bulk_test + run_functional_test + run_ping_test + run_signal_test) != 1) usage(); if (argn < argc) { g_params.iters = atoi(argv[argn++]); if (argn != argc) { usage(); } } vcos_log_set_level(VCOS_LOG_CATEGORY, verbose ? VCOS_LOG_TRACE : VCOS_LOG_INFO); vcos_log_register("vchiq_test", VCOS_LOG_CATEGORY); #ifdef VCHIQ_LOCAL { static VCOS_THREAD_T server_task; void *pointer = NULL; int stack_size = 4096; #if VCOS_CAN_SET_STACK_ADDR pointer = malloc(stack_size); vcos_demand(pointer); #endif vcos_thread_create_classic(&server_task, "vchiq_test server", vchiq_test_server, (void *)0, pointer, stack_size, 10 | VCOS_AFFINITY_CPU1, 20, VCOS_START); } #endif vcos_event_create(&g_server_reply, "g_server_reply"); vcos_event_create(&g_shutdown, "g_shutdown"); vcos_mutex_create(&g_mutex, "g_mutex"); status = VCHIQ_ERROR; if (run_bulk_test) status = vchiq_bulk_test(); else if (run_ctrl_test) status = vchiq_ctrl_test(); else if (run_functional_test) status = vchiq_functional_test(); else if (run_ping_test) status = vchiq_ping_test(); else if (run_signal_test) status = vchiq_signal_test(); return (status == VCHIQ_SUCCESS) ? 0 : -1; } static VCHIQ_STATUS_T vchiq_bulk_test(void) { VCHIQ_INSTANCE_T vchiq_instance; VCHIQ_SERVICE_HANDLE_T vchiq_service; VCHIQ_SERVICE_PARAMS_T service_params; VCHIQ_ELEMENT_T elements[4]; VCHIQ_ELEMENT_T *element; int num_bulk_bufs = NUM_BULK_BUFS; uint32_t start, end; int i; g_params.blocksize *= 1024; for (i = 0; i < (NUM_BULK_BUFS * 2); i++) { bulk_bufs[i] = malloc(g_params.blocksize + BULK_ALIGN_SIZE - 1); if (!bulk_bufs[i]) { printf("* out of memory\n"); while (i > 0) { free(bulk_bufs[--i]); } return VCHIQ_ERROR; } } for (i = 0; i < NUM_BULK_BUFS; i++) { int j; bulk_tx_data[i] = buf_align(bulk_bufs[i*2 + 0], g_params.align_size, g_params.client_align); bulk_rx_data[i] = buf_align(bulk_bufs[i*2 + 1], g_params.align_size, g_params.client_align); for (j = 0; j < g_params.blocksize; j+=4) { *(unsigned int *)(bulk_tx_data[i] + j) = ((0x80 | i) << 24) + j; } memset(bulk_rx_data[i], 0xff, g_params.blocksize); } #ifdef ANDROID if (g_timeout_ms) { setup_auto_kill(g_timeout_ms); } #endif if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) { printf("* failed to open vchiq instance\n"); return VCHIQ_ERROR; } vchiq_connect(vchiq_instance); memset(&service_params, 0, sizeof(service_params)); service_params.version = service_params.version_min = VCHIQ_TEST_VER; service_params.fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); service_params.callback = clnt_callback; service_params.userdata = "clnt userdata"; if (vchiq_open_service(vchiq_instance, &service_params, &vchiq_service) != VCHIQ_SUCCESS) { printf("* failed to open service - already in use?\n"); return VCHIQ_ERROR; } printf("Bulk test - service:%s, block size:%d, iters:%d\n", g_servname, g_params.blocksize, g_params.iters); /* coverity[missing_lock : FALSE] - g_server_reply is not used for mutual exclusion */ g_params.echo = want_echo; element = elements; element->data = &g_params; element->size = sizeof(g_params); element++; vchiq_queue_message(vchiq_service, elements, element - elements); vcos_event_wait(&g_server_reply); if (g_server_error) { printf("* server error: %s\n", g_server_error); return VCHIQ_ERROR; } if ( num_bulk_bufs > g_params.iters ) num_bulk_bufs = g_params.iters; start = vcos_getmicrosecs(); vcos_mutex_lock(&g_mutex); for (i = 0; i < num_bulk_bufs; i++) { vchiq_queue_bulk_transmit(vchiq_service, bulk_tx_data[i], g_params.blocksize, (void *)i); vcos_log_trace("vchiq_test: queued bulk tx %d", i); bulk_tx_sent++; if (g_params.echo) { vchiq_queue_bulk_receive(vchiq_service, bulk_rx_data[i], g_params.blocksize, (void *)i); vcos_log_trace("vchiq_test: queued bulk rx %d", i); bulk_rx_sent++; } } vcos_mutex_unlock(&g_mutex); vcos_log_trace("Sent all messages"); vcos_log_trace("vchiq_test: waiting for shutdown"); vcos_event_wait(&g_shutdown); end = vcos_getmicrosecs(); for (i = 0; i < (NUM_BULK_BUFS * 2); i++) { free(bulk_bufs[i]); } vchiq_remove_service(vchiq_service); vcos_log_trace("vchiq_test: shutting down"); vchiq_shutdown(vchiq_instance); printf("Elapsed time: %dus per iteration\n", (end - start) / g_params.iters); return VCHIQ_SUCCESS; } static VCHIQ_STATUS_T vchiq_ctrl_test(void) { VCHIQ_INSTANCE_T vchiq_instance; VCHIQ_SERVICE_HANDLE_T vchiq_service; VCHIQ_SERVICE_PARAMS_T service_params; uint32_t start, end; int i; ctrl_received = 0; if (g_params.blocksize < 4) g_params.blocksize = 4; for (i = 0; i < NUM_BULK_BUFS; i++) { int j; bulk_tx_data[i] = malloc(g_params.blocksize); if (!bulk_tx_data[i]) { printf("* out of memory\n"); return VCHIQ_ERROR; } *(int *)bulk_tx_data[i] = MSG_ECHO; for (j = 4; j < g_params.blocksize; j+=4) { *(unsigned int *)(bulk_tx_data[i] + j) = ((0x80 | i) << 24) + j; } } #ifdef ANDROID if (g_timeout_ms) { setup_auto_kill(g_timeout_ms); } #endif if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) { printf("* failed to open vchiq instance\n"); return VCHIQ_ERROR; } vchiq_connect(vchiq_instance); memset(&service_params, 0, sizeof(service_params)); service_params.fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); service_params.callback = clnt_callback; service_params.userdata = "clnt userdata"; service_params.version = VCHIQ_TEST_VER; service_params.version_min = VCHIQ_TEST_VER; if (vchiq_open_service(vchiq_instance, &service_params, &vchiq_service) != VCHIQ_SUCCESS) { printf("* failed to open service - already in use?\n"); return VCHIQ_ERROR; } printf("Ctrl test - service:%s, block size:%d, iters:%d\n", g_servname, g_params.blocksize, g_params.iters); start = vcos_getmicrosecs(); for (i = 0; i < g_params.iters; i++) { VCHIQ_ELEMENT_T element; element.data = bulk_tx_data[i % NUM_BULK_BUFS]; element.size = g_params.blocksize; if (vchiq_queue_message(vchiq_service, &element, 1) != VCHIQ_SUCCESS) { printf("* failed to send a message\n"); goto error_exit; } if (g_server_error) { printf("* error - %s\n", g_server_error); goto error_exit; } } vcos_log_trace("Sent all messages"); if (g_params.echo) { vcos_log_trace("vchiq_test: waiting for shutdown"); vcos_event_wait(&g_shutdown); } if (g_server_error) { printf("* error - %s\n", g_server_error); goto error_exit; } end = vcos_getmicrosecs(); vchiq_remove_service(vchiq_service); vcos_log_trace("vchiq_test: shutting down"); vchiq_shutdown(vchiq_instance); printf("Elapsed time: %dus per iteration\n", (end - start) / g_params.iters); return VCHIQ_SUCCESS; error_exit: vchiq_remove_service(vchiq_service); vchiq_shutdown(vchiq_instance); return VCHIQ_ERROR; } static VCHIQ_STATUS_T vchiq_functional_test(void) { int i; printf("Functional test - iters:%d\n", g_params.iters); for (i = 0; i < g_params.iters; i++) { printf("======== iteration %d ========\n", i+1); if (do_functional_test() != VCHIQ_SUCCESS) return VCHIQ_ERROR; } return VCHIQ_SUCCESS; } static VCHIQ_STATUS_T vchiq_ping_test(void) { /* Measure message round trip time for various sizes*/ VCHIQ_INSTANCE_T vchiq_instance; VCHIQ_SERVICE_HANDLE_T vchiq_service; VCHI_SERVICE_HANDLE_T vchi_service; SERVICE_CREATION_T service_params; VCHIQ_SERVICE_PARAMS_T vchiq_service_params; int fourcc; static int sizes[] = { 0, 1024, 2048, VCHIQ_MAX_MSG_SIZE }; unsigned int i; fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); printf("Ping test - service:%s, iters:%d, version %d\n", g_servname, g_params.iters, VCHIQ_TEST_VER); #ifdef ANDROID if (g_timeout_ms) { setup_auto_kill(g_timeout_ms); } #endif if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) { printf("* failed to open vchiq instance\n"); return VCHIQ_ERROR; } vchiq_connect(vchiq_instance); memset(&service_params, 0, sizeof(service_params)); service_params.version.version = service_params.version.version_min = VCHIQ_TEST_VER; service_params.service_id = fourcc; service_params.callback = vchi_clnt_callback; service_params.callback_param = &vchi_service; if (vchi_service_open((VCHI_INSTANCE_T)vchiq_instance, &service_params, &vchi_service) != VCHIQ_SUCCESS) { printf("* failed to open service - already in use?\n"); return VCHIQ_ERROR; } for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) { const int iter_count = g_params.iters; do_vchi_ping_test(vchi_service, sizes[i], 0, 0, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 0, 0, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 1, 0, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 2, 0, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 10, 0, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 0, 1, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 0, 2, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 0, 10, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 10, 10, iter_count); do_vchi_ping_test(vchi_service, sizes[i], 100, 0, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 0, 100, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 100, 100, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 200, 0, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 0, 200, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 200, 200, iter_count/10); do_vchi_ping_test(vchi_service, sizes[i], 400, 0, iter_count/20); do_vchi_ping_test(vchi_service, sizes[i], 0, 400, iter_count/20); do_vchi_ping_test(vchi_service, sizes[i], 400, 400, iter_count/20); do_vchi_ping_test(vchi_service, sizes[i], 1000, 0, iter_count/50); do_vchi_ping_test(vchi_service, sizes[i], 0, 1000, iter_count/50); do_vchi_ping_test(vchi_service, sizes[i], 1000, 1000, iter_count/50); } vchi_service_close(vchi_service); INIT_PARAMS(&vchiq_service_params, fourcc, clnt_callback, "clnt userdata", VCHIQ_TEST_VER); if (vchiq_open_service(vchiq_instance, &vchiq_service_params, &vchiq_service) != VCHIQ_SUCCESS) { printf("* failed to open service - already in use?\n"); return VCHIQ_ERROR; } for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) { const int iter_count = g_params.iters; do_ping_test(vchiq_service, sizes[i], 0, 0, iter_count); do_ping_test(vchiq_service, sizes[i], 0, 0, iter_count); do_ping_test(vchiq_service, sizes[i], 1, 0, iter_count); do_ping_test(vchiq_service, sizes[i], 2, 0, iter_count); do_ping_test(vchiq_service, sizes[i], 10, 0, iter_count); do_ping_test(vchiq_service, sizes[i], 0, 1, iter_count); do_ping_test(vchiq_service, sizes[i], 0, 2, iter_count); do_ping_test(vchiq_service, sizes[i], 0, 10, iter_count); do_ping_test(vchiq_service, sizes[i], 10, 10, iter_count); do_ping_test(vchiq_service, sizes[i], 100, 0, iter_count/10); do_ping_test(vchiq_service, sizes[i], 0, 100, iter_count/10); do_ping_test(vchiq_service, sizes[i], 100, 100, iter_count/10); do_ping_test(vchiq_service, sizes[i], 200, 0, iter_count/10); do_ping_test(vchiq_service, sizes[i], 0, 200, iter_count/10); do_ping_test(vchiq_service, sizes[i], 200, 200, iter_count/10); do_ping_test(vchiq_service, sizes[i], 400, 0, iter_count/20); do_ping_test(vchiq_service, sizes[i], 0, 400, iter_count/20); do_ping_test(vchiq_service, sizes[i], 400, 400, iter_count/20); do_ping_test(vchiq_service, sizes[i], 1000, 0, iter_count/50); do_ping_test(vchiq_service, sizes[i], 0, 1000, iter_count/50); do_ping_test(vchiq_service, sizes[i], 1000, 1000, iter_count/50); } vchiq_close_service(vchiq_service); return VCHIQ_SUCCESS; } static VCHIQ_STATUS_T vchiq_signal_test(void) { /* Measure message round trip time for various sizes*/ VCHIQ_INSTANCE_T vchiq_instance; VCHIQ_SERVICE_HANDLE_T vchiq_service; VCHIQ_SERVICE_PARAMS_T vchiq_service_params; int fourcc; static int sizes[] = { 0, 1024, 2048, VCHIQ_MAX_MSG_SIZE }; fourcc = VCHIQ_MAKE_FOURCC(g_servname[0], g_servname[1], g_servname[2], g_servname[3]); printf("signal test - service:%s, iters:%d, version %d\n", g_servname, g_params.iters, VCHIQ_TEST_VER); #ifdef ANDROID if (g_timeout_ms) { setup_auto_kill(g_timeout_ms); } #endif if (vchiq_initialise(&vchiq_instance) != VCHIQ_SUCCESS) { printf("* failed to open vchiq instance\n"); return VCHIQ_ERROR; } vchiq_connect(vchiq_instance); INIT_PARAMS(&vchiq_service_params, fourcc, clnt_callback, "clnt userdata", VCHIQ_TEST_VER); if (vchiq_open_service(vchiq_instance, &vchiq_service_params, &vchiq_service) != VCHIQ_SUCCESS) { printf("* failed to open service - already in use?\n"); return VCHIQ_ERROR; } vchiq_bulk_transmit(vchiq_service, &sizes, 16, 0, VCHIQ_BULK_MODE_BLOCKING); vchiq_close_service(vchiq_service); return VCHIQ_SUCCESS; } static VCHIQ_STATUS_T do_functional_test(void) { VCHIQ_ELEMENT_T elements[4]; VCHIQ_INSTANCE_T instance; VCHIQ_SERVICE_HANDLE_T service, service2, service3; VCHIQ_SERVICE_PARAMS_T service_params; VCHIQ_CONFIG_T config; unsigned int size, i; vcos_event_create(&func_test_sync, "test_sync"); #ifdef ANDROID if (g_timeout_ms) { setup_auto_kill(g_timeout_ms); } #endif if (func_data_test_start != -1) goto bulk_tests_only; EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); EXPECT(vchiq_get_config(instance, sizeof(config) - 1, &config), VCHIQ_SUCCESS); // too small, but allowed for backwards compatibility EXPECT(vchiq_get_config(instance, sizeof(config) + 1, &config), VCHIQ_ERROR); // too large EXPECT(vchiq_get_config(instance, sizeof(config), &config), VCHIQ_SUCCESS); // just right EXPECT(config.max_msg_size, VCHIQ_MAX_MSG_SIZE); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void *)1, VCHIQ_TEST_VER); EXPECT(vchiq_add_service(instance, &service_params, &service), VCHIQ_SUCCESS); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void *)2, VCHIQ_TEST_VER); EXPECT(vchiq_add_service(instance, &service_params, &service2), VCHIQ_SUCCESS); INIT_PARAMS(&service_params, FUNC_FOURCC, clnt_callback, (void *)3, VCHIQ_TEST_VER); EXPECT(vchiq_add_service(instance, &service_params, &service3), VCHIQ_ERROR); // callback doesn't match EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0), VCHIQ_SUCCESS); EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 1), VCHIQ_SUCCESS); EXPECT(vchiq_set_service_option(service, 42, 1), VCHIQ_ERROR); // invalid option EXPECT(vchiq_remove_service(service), VCHIQ_SUCCESS); EXPECT(vchiq_remove_service(service), VCHIQ_ERROR); // service already removed EXPECT(vchiq_remove_service(service2), VCHIQ_SUCCESS); EXPECT(vchiq_queue_message(service, NULL, 0), VCHIQ_ERROR); // service not valid EXPECT(vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_AUTOCLOSE, 0), VCHIQ_ERROR); // service not valid INIT_PARAMS(&service_params, FUNC_FOURCC, clnt_callback, (void *)3, VCHIQ_TEST_VER); EXPECT(vchiq_add_service(instance, &service_params, &service3), VCHIQ_SUCCESS); EXPECT(vchiq_queue_message(service, NULL, 0), VCHIQ_ERROR); // service not open EXPECT(vchiq_queue_bulk_transmit(service, clnt_service1_data, sizeof(clnt_service1_data), (void *)1), VCHIQ_ERROR); // service not open EXPECT(vchiq_queue_bulk_receive(service2, clnt_service2_data, sizeof(clnt_service2_data), (void *)2), VCHIQ_ERROR); // service not open EXPECT(vchiq_queue_bulk_receive(service, 0, sizeof(clnt_service1_data), (void *)1), VCHIQ_ERROR); // invalid buffer EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)1, 0); EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // not connected EXPECT(vchiq_connect(instance), VCHIQ_SUCCESS); EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // wrong version number memset(&service_params, 0, sizeof(service_params)); service_params.fourcc = FUNC_FOURCC; service_params.callback = func_clnt_callback; service_params.userdata = (void*)1; service_params.version = 1; service_params.version_min = 1; EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // Still the wrong version number service_params.version = VCHIQ_TEST_VER + 1; service_params.version_min = VCHIQ_TEST_VER + 1; EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); // Still the wrong version number service_params.version = VCHIQ_TEST_VER; service_params.version_min = VCHIQ_TEST_VER; EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_SUCCESS); // That's better INIT_PARAMS(&service_params, VCHIQ_MAKE_FOURCC('n','o','n','e'), func_clnt_callback, (void*)2, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_ERROR); // no listener INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)2, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_SUCCESS); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)3, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service3), VCHIQ_ERROR); // no more listeners EXPECT(vchiq_remove_service(service2), VCHIQ_SUCCESS); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, (void*)2, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service2), VCHIQ_SUCCESS); elements[0].data = "a"; elements[0].size = 1; elements[1].data = "bcdef"; elements[1].size = 5; elements[2].data = "ghijklmnopq"; elements[2].size = 11; elements[3].data = "rstuvwxyz"; elements[3].size = 9; EXPECT(vchiq_queue_message(service, elements, 4), VCHIQ_SUCCESS); EXPECT(vchiq_queue_bulk_transmit(service2, clnt_service2_data, sizeof(clnt_service2_data), (void *)0x2001), VCHIQ_SUCCESS); for (i = 0; i < sizeof(clnt_service1_data); i++) { clnt_service1_data[i] = (char)i; } EXPECT(vchiq_queue_bulk_transmit(service, clnt_service1_data, sizeof(clnt_service1_data), (void*)0x1001), VCHIQ_SUCCESS); vcos_event_wait(&func_test_sync); EXPECT(func_error, 0); EXPECT(vchiq_remove_service(service), VCHIQ_SUCCESS); vcos_event_wait(&func_test_sync); EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); vcos_event_wait(&func_test_sync); EXPECT(func_error, 0); INIT_PARAMS(&service_params, FUNC_FOURCC, func_clnt_callback, NULL, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_ERROR); /* Instance not initialised */ EXPECT(vchiq_add_service(instance, &service_params, &service), VCHIQ_ERROR); /* Instance not initialised */ EXPECT(vchiq_connect(instance), VCHIQ_ERROR); /* Instance not initialised */ bulk_tests_only: /* Now test the bulk data transfers */ EXPECT(vchiq_initialise(&instance), VCHIQ_SUCCESS); EXPECT(vchiq_connect(instance), VCHIQ_SUCCESS); func_data_test_iter = 0; INIT_PARAMS(&service_params, FUN2_FOURCC, fun2_clnt_callback, NULL, VCHIQ_TEST_VER); EXPECT(vchiq_open_service(instance, &service_params, &service), VCHIQ_SUCCESS); if (func_data_test_end < func_data_test_start) goto skip_bulk_tests; printf("Testing bulk transfer for alignment.\n"); for (size = 1; size < 64; size++) { int align, srvr_align; for (srvr_align = 32; srvr_align; srvr_align >>= 1) { for (align = 32; align; align >>= 1) { EXPECT(func_data_test(service, size, align & 31, srvr_align & 31), VCHIQ_SUCCESS); } } } printf("Testing bulk transfer at PAGE_SIZE.\n"); for (size = 1; size < 64; size++) { int align, srvr_align; for (srvr_align = 32; srvr_align; srvr_align >>= 1) { for (align = 32; align; align >>= 1) { EXPECT(func_data_test(service, size, PAGE_SIZE - align, srvr_align & 31), VCHIQ_SUCCESS); } } } for (size = 64; size < FUN2_MAX_DATA_SIZE; size<<=1) { static const int aligns[] = { 0, 1, 31 }; for (i = 0; i < vcos_countof(aligns); i++) { int srvr_align = aligns[i]; unsigned int j; for (j = 0; j < vcos_countof(aligns); j++) { int k; int align = aligns[j]; for (k = 0; k <= 8; k++) { EXPECT(func_data_test(service, size, align, srvr_align + k), VCHIQ_SUCCESS); } } } } skip_bulk_tests: EXPECT(vchiq_shutdown(instance), VCHIQ_SUCCESS); vcos_event_delete(&func_test_sync); return VCHIQ_SUCCESS; error_exit: return VCHIQ_ERROR; } static void do_ping_test(VCHIQ_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters) { uint32_t start, end; char *ping_buf = malloc(size + sizeof(struct test_params)); struct test_params *params = (struct test_params *)ping_buf; VCHIQ_ELEMENT_T element; int i; element.data = ping_buf; /* Set up the quotas for messages */ *params = g_params; params->magic = MSG_CONFIG; params->blocksize = 0; element.size = sizeof(*params); vchiq_queue_message(service, &element, 1); vcos_event_wait(&g_server_reply); vchiq_set_service_option(service, VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, params->client_message_quota); /* Allow enough room for the type header */ element.size = (size < 4) ? 4 : size; bulk_tx_received = -1; start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { int j; for (j = 0; j < vcos_max(async, oneway); j++) { if (j < async) { params->magic = MSG_ASYNC; vchiq_queue_message(service, &element, 1); } if (j < oneway) { params->magic = MSG_ONEWAY; vchiq_queue_message(service, &element, 1); } } params->magic = MSG_SYNC; vchiq_queue_message(service, &element, 1); vcos_event_wait(&g_server_reply); } end = vcos_getmicrosecs(); printf("ping (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); vcos_sleep(20); if ((async == 0) && (oneway == 0)) { *params = g_params; params->magic = MSG_CONFIG; params->blocksize = size ? size : 8; params->iters = iters; params->verify = 0; params->echo = 0; element.size = sizeof(*params); vchiq_queue_message(service, &element, 1); vcos_event_wait(&g_server_reply); vcos_sleep(30); start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { vchiq_queue_bulk_transmit(service, ping_buf, params->blocksize, 0); vcos_event_wait(&g_server_reply); } end = vcos_getmicrosecs(); printf("bulk (size %d, async) -> %fus\n", size, ((float)(end - start))/iters); vcos_sleep(40); } if (oneway == 0) { *params = g_params; params->magic = MSG_CONFIG; params->blocksize = size ? size : 8; params->iters = iters * (async + 1); params->verify = 0; params->echo = 0; element.size = sizeof(*params); vchiq_queue_message(service, &element, 1); vcos_event_wait(&g_server_reply); vcos_sleep(50); start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { int j; for (j = 0; j < async; j++) vchiq_bulk_transmit(service, ping_buf, params->blocksize, 0, VCHIQ_BULK_MODE_NOCALLBACK); vchiq_bulk_transmit(service, ping_buf, params->blocksize, 0, VCHIQ_BULK_MODE_BLOCKING); } end = vcos_getmicrosecs(); printf("bulk (size %d, %d async) -> %fus\n", size, async, ((float)(end - start))/iters); vcos_sleep(60); } free(ping_buf); bulk_tx_received = 0; } static void do_vchi_ping_test(VCHI_SERVICE_HANDLE_T service, int size, int async, int oneway, int iters) { uint32_t start, end; uint32_t actual; char *ping_buf = malloc(size + sizeof(struct test_params)); char pong_buf[100]; struct test_params *params = (struct test_params *)ping_buf; int msg_size; int i; /* Set up the quotas for messages */ *params = g_params; params->magic = MSG_CONFIG; params->blocksize = 0; vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vcos_event_wait(&g_server_reply); vchiq_set_service_option((VCHIQ_SERVICE_HANDLE_T)service, VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, params->client_message_quota); /* Allow enough room for the type header */ msg_size = (size < 4) ? 4 : size; bulk_tx_received = -1; if ((oneway == 0) && (async == 0)) { params->magic = MSG_SYNC; g_sync_mode = 1; start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); } end = vcos_getmicrosecs(); printf("vchi ping (size %d) -> %fus\n", size, ((float)(end - start))/iters); vcos_sleep(10); g_sync_mode = 0; } while (vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_NONE) != -1) { printf("* Unexpected message found in queue - size %d\n", actual); } start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { int j; for (j = 0; j < vcos_max(async, oneway); j++) { if (j < async) { params->magic = MSG_ASYNC; vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); } if (j < oneway) { params->magic = MSG_ONEWAY; vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); } } params->magic = MSG_SYNC; vchi_msg_queue(service, ping_buf, msg_size, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vcos_event_wait(&g_server_reply); } end = vcos_getmicrosecs(); printf("vchi ping (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); vcos_sleep(20); if ((async == 0) && (oneway == 0)) { *params = g_params; params->magic = MSG_CONFIG; params->blocksize = size ? size : 8; params->iters = iters; params->verify = 0; params->echo = 0; vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vcos_event_wait(&g_server_reply); vcos_sleep(30); start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vcos_event_wait(&g_server_reply); } end = vcos_getmicrosecs(); printf("vchi bulk (size %d, %d async, %d oneway) -> %fus\n", size, async, oneway, ((float)(end - start))/iters); vcos_sleep(40); } if (oneway == 0) { *params = g_params; params->magic = MSG_CONFIG; params->blocksize = size ? size : 8; params->iters = iters * (async + 1); params->verify = 0; params->echo = 0; vchi_msg_queue(service, params, sizeof(*params), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, 0); vcos_event_wait(&g_server_reply); vcos_sleep(50); start = vcos_getmicrosecs(); for (i = 0; i < iters; i++) { int j; for (j = 0; j < async; j++) vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, VCHI_FLAGS_NONE, 0); vchi_bulk_queue_transmit(service, ping_buf, params->blocksize, VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, 0); } end = vcos_getmicrosecs(); printf("vchi bulk (size %d, %d oneway) -> %fus\n", size, oneway, ((float)(end - start))/iters); vcos_sleep(60); } free(ping_buf); bulk_tx_received = 0; } static VCHIQ_STATUS_T func_data_test(VCHIQ_SERVICE_HANDLE_T service, int datalen, int align, int server_align) { enum { PROLOGUE_SIZE = 32, EPILOGUE_SIZE = 32 }; static uint8_t databuf[PAGE_SIZE + PROLOGUE_SIZE + FUN2_MAX_DATA_SIZE + EPILOGUE_SIZE]; static uint8_t databuf2[PAGE_SIZE + PROLOGUE_SIZE + FUN2_MAX_DATA_SIZE + EPILOGUE_SIZE]; uint8_t *data, *data2, *prologue, *epilogue; VCHIQ_ELEMENT_T element; int params[4] = { datalen, server_align, align, func_data_test_iter }; int success = 1, i; if (!vcos_verify(datalen < FUN2_MAX_DATA_SIZE)) return VCHIQ_ERROR; if ((func_data_test_iter < func_data_test_start) || (func_data_test_iter > func_data_test_end)) goto skip_iter; element.size = sizeof(params); element.data = ¶ms; EXPECT(vchiq_queue_message(service, &element, 1), VCHIQ_SUCCESS); memset(databuf, 0xff, sizeof(databuf)); data = (uint8_t *)((uintptr_t)databuf & ~(PAGE_SIZE - 1)) + align; data = (uint8_t *)((((intptr_t)databuf + PROLOGUE_SIZE) & ~(FUN2_MAX_ALIGN - 1)) + align - PROLOGUE_SIZE); if (data < databuf) data += PAGE_SIZE; data += PROLOGUE_SIZE; EXPECT(vchiq_queue_bulk_receive(service, data, datalen, NULL), VCHIQ_SUCCESS); data2 = (uint8_t *)(((uintptr_t)databuf2 + PROLOGUE_SIZE) & ~(PAGE_SIZE - 1)) + align - PROLOGUE_SIZE; if (data2 < databuf2) data2 += PAGE_SIZE; prologue = data2; data2 += PROLOGUE_SIZE; epilogue = data2 + datalen; memset(prologue, 0xff, PROLOGUE_SIZE); memset(epilogue, 0xff, EPILOGUE_SIZE); for (i = 0; i < (datalen - 1); i++) { data2[i] = (uint8_t)(((i & 0x1f) == 0) ? (i >> 5) : i); } data2[i] = '\0'; /* Attempt to pull the boundaries into the cache */ prologue = data - PROLOGUE_SIZE; epilogue = data + datalen; prologue[PROLOGUE_SIZE - 1] = 0xfe; epilogue[0] = 0xfe; EXPECT(vchiq_queue_bulk_transmit(service, data2, datalen, NULL), VCHIQ_SUCCESS); vcos_event_wait(&func_test_sync); /* Wait for the receive to complete */ for (i = 0; i < PROLOGUE_SIZE; i++) { if (prologue[i] != (uint8_t)((i == PROLOGUE_SIZE - 1) ? '\xfe' : '\xff')) { vcos_log_error("%d: Prologue corrupted at %x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i, datalen, align, server_align, prologue[i]); VCOS_BKPT; success = 0; break; } } for (i = 0; i < EPILOGUE_SIZE; i++) { if (epilogue[i] != (uint8_t)((i == 0) ? '\xfe' : '\xff')) { vcos_log_error("%d: Epilogue corrupted at %x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i, datalen, align, server_align, epilogue[i]); VCOS_BKPT; success = 0; break; } } if (success) { int diffs = 0; for (i = 0; i < datalen; i++) { int diff = (i == datalen - 1) ? (data[i] != 0) : ((i & 0x1f) == 0) ? (data[i] != (uint8_t)(i >> 5)) : (data[i] != (uint8_t)i); if (diff) diffs++; else if (diffs) { vcos_log_error("%d: Data corrupted at %x-%x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i - diffs, i - 1, datalen, align, server_align, data[i-1]); VCOS_BKPT; success = 0; diffs = 0; } } if (diffs) { vcos_log_error("%d: Data corrupted at %x-%x (datalen %x, align %x, server_align %x) -> %02x", func_data_test_iter, i - diffs, i - 1, datalen, align, server_align, data[i-1]); VCOS_BKPT; success = 0; } } skip_iter: if (success) { func_data_test_iter++; return VCHIQ_SUCCESS; } error_exit: return VCHIQ_ERROR; } #ifdef VCHIQ_LOCAL static void *vchiq_test_server(void *param) { VCHIQ_INSTANCE_T instance; vcos_demand(vchiq_initialise(&instance) == VCHIQ_SUCCESS); vchiq_test_start_services(instance); vchiq_connect(instance); printf("test server started\n"); return 0; } #endif static VCHIQ_STATUS_T clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) { int data; vcos_mutex_lock(&g_mutex); if (reason == VCHIQ_MESSAGE_AVAILABLE) { /* * Store the header size as it is going to be released * and the size may be overwritten by the release. */ size_t header_size = header->size; if (header_size <= 1) vchiq_release_message(service, header); else /* Responses of length 0 are not sync points */ if ((header_size >= 4) && (memcpy(&data, header->data, sizeof(data)), data == MSG_ECHO)) { /* This is a complete echoed packet */ if (g_params.verify && (mem_check(header->data, bulk_tx_data[ctrl_received % NUM_BULK_BUFS], g_params.blocksize) != 0)) g_server_error = "corrupt data"; else ctrl_received++; if (g_server_error || (ctrl_received == g_params.iters)) vcos_event_signal(&g_shutdown); vchiq_release_message(service, header); } else if (header_size != 0) g_server_error = header->data; if ((header_size != 0) || g_server_error) vcos_event_signal(&g_server_reply); } else if (reason == VCHIQ_BULK_TRANSMIT_DONE) { int i = (int)bulk_userdata; vcos_log_trace(" BULK_TRANSMIT_DONE(%d)", i); if (bulk_tx_received < 0) vcos_event_signal(&g_server_reply); else { vcos_assert(i == bulk_tx_received); bulk_tx_received++; if (bulk_tx_sent < g_params.iters) { vchiq_queue_bulk_transmit(service, bulk_tx_data[i % NUM_BULK_BUFS], g_params.blocksize, (void *)bulk_tx_sent); bulk_tx_sent++; } } } else if (reason == VCHIQ_BULK_RECEIVE_DONE) { int i = (int)bulk_userdata; vcos_log_trace(" BULK_RECEIVE_DONE(%d): data '%s'", i, bulk_rx_data[i % NUM_BULK_BUFS]); vcos_assert(i == bulk_rx_received); if (g_params.verify && (mem_check(bulk_tx_data[i % NUM_BULK_BUFS], bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize) != 0)) { vcos_log_error("* Data corruption - %d: %x, %x, %x", i, (unsigned int)bulk_tx_data[i % NUM_BULK_BUFS], (unsigned int)bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize); VCOS_BKPT; } bulk_rx_received++; if (bulk_rx_sent < g_params.iters) { if (g_params.verify) memset(bulk_rx_data[i % NUM_BULK_BUFS], 0xff, g_params.blocksize); vchiq_queue_bulk_receive(service, bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize, (void *)bulk_rx_sent); bulk_rx_sent++; } } else if (reason == VCHIQ_BULK_TRANSMIT_ABORTED) { int i = (int)bulk_userdata; vcos_log_info(" BULK_TRANSMIT_ABORTED(%d)", i); } else if (reason == VCHIQ_BULK_RECEIVE_ABORTED) { int i = (int)bulk_userdata; vcos_log_info(" BULK_RECEIVE_ABORTED(%d)", i); } if ((bulk_tx_received == g_params.iters) && ((g_params.echo == 0) || (bulk_rx_received == g_params.iters))) vcos_event_signal(&g_shutdown); vcos_mutex_unlock(&g_mutex); return VCHIQ_SUCCESS; } static void vchi_clnt_callback(void *callback_param, VCHI_CALLBACK_REASON_T reason, void *handle) { VCHI_SERVICE_HANDLE_T service = *(VCHI_SERVICE_HANDLE_T *)callback_param; vcos_mutex_lock(&g_mutex); if (reason == VCHI_CALLBACK_MSG_AVAILABLE) { if (!g_sync_mode) { static char pong_buf[100]; uint32_t actual; while (vchi_msg_dequeue(service, pong_buf, sizeof(pong_buf), &actual, VCHI_FLAGS_NONE) == 0) { if (actual > 1) g_server_error = pong_buf; if (actual != 0) { /* Responses of length 0 are not sync points */ vcos_event_signal(&g_server_reply); break; } } } } else if (reason == VCHI_CALLBACK_BULK_SENT) { int i = (int)handle; vcos_log_trace(" BULK_TRANSMIT_DONE(%d)", i); if (bulk_tx_received < 0) vcos_event_signal(&g_server_reply); else { vcos_assert(i == bulk_tx_received); bulk_tx_received++; if (bulk_tx_sent < g_params.iters) { vchi_bulk_queue_transmit(service, bulk_tx_data[i % NUM_BULK_BUFS], g_params.blocksize, VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, (void *)bulk_tx_sent); bulk_tx_sent++; } } } else if (reason == VCHI_CALLBACK_BULK_RECEIVED) { int i = (int)handle; vcos_log_trace(" BULK_RECEIVE_DONE(%d): data '%s'", i, bulk_rx_data[i % NUM_BULK_BUFS]); vcos_assert(i == bulk_rx_received); if (g_params.verify && (mem_check(bulk_tx_data[i % NUM_BULK_BUFS], bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize) != 0)) { vcos_log_error("* Data corruption - %x, %x, %x", (unsigned int)bulk_tx_data[i % NUM_BULK_BUFS], (unsigned int)bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize); VCOS_BKPT; } bulk_rx_received++; if (bulk_rx_sent < g_params.iters) { if (g_params.verify) memset(bulk_rx_data[i % NUM_BULK_BUFS], 0xff, g_params.blocksize); vchi_bulk_queue_receive(service, bulk_rx_data[i % NUM_BULK_BUFS], g_params.blocksize, VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED, (void *)bulk_rx_sent); bulk_rx_sent++; } } else if (reason == VCHI_CALLBACK_BULK_TRANSMIT_ABORTED) { int i = (int)handle; vcos_log_info(" BULK_TRANSMIT_ABORTED(%d)", i); } else if (reason == VCHI_CALLBACK_BULK_RECEIVE_ABORTED) { int i = (int)handle; vcos_log_info(" BULK_RECEIVE_ABORTED(%d)", i); } if ((bulk_tx_received == g_params.iters) && (bulk_rx_received == g_params.iters)) vcos_event_signal(&g_shutdown); vcos_mutex_unlock(&g_mutex); } static VCHIQ_STATUS_T func_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) { static int callback_count = 0, bulk_count = 0; int callback_index = 0, bulk_index = 0; if (reason < VCHIQ_BULK_TRANSMIT_DONE) { callback_count++; START_CALLBACK(VCHIQ_SERVICE_CLOSED, 2) END_CALLBACK(VCHIQ_SUCCESS) START_CALLBACK(VCHIQ_MESSAGE_AVAILABLE, 1) EXPECT(bulk_userdata, NULL); EXPECT(header->size, 26); EXPECT(mem_check(header->data, "abcdefghijklmnopqrstuvwxyz", 26), 0); vchiq_release_message(service, header); END_CALLBACK(VCHIQ_SUCCESS) START_CALLBACK(VCHIQ_MESSAGE_AVAILABLE, 1) EXPECT(bulk_userdata, NULL); EXPECT(header->size, 0); vchiq_release_message(service, header); EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1004), VCHIQ_SUCCESS); vcos_event_signal(&func_test_sync); END_CALLBACK(VCHIQ_SUCCESS) START_CALLBACK(VCHIQ_SERVICE_CLOSED, 1) vcos_event_signal(&func_test_sync); END_CALLBACK(VCHIQ_SUCCESS) START_CALLBACK(VCHIQ_SERVICE_CLOSED, 2) vcos_event_signal(&func_test_sync); callback_count = 0; bulk_count = 0; END_CALLBACK(VCHIQ_SUCCESS) } else { bulk_count++; START_BULK_CALLBACK(VCHIQ_BULK_TRANSMIT_DONE, 1, 0x1001) memset(clnt_service2_data, 0xff, sizeof(clnt_service2_data)); EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1002), VCHIQ_SUCCESS); END_CALLBACK(VCHIQ_SUCCESS) START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_ABORTED, 1, 0x1002) EXPECT(vchiq_queue_bulk_receive(service, clnt_service2_data, sizeof(clnt_service2_data), (void*)0x1003), VCHIQ_SUCCESS); END_CALLBACK(VCHIQ_SUCCESS) START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_DONE, 1, 0x1003) (void)(mem_check(clnt_service1_data, clnt_service2_data, sizeof(clnt_service1_data)), 0); (void)(mem_check(clnt_service1_data, clnt_service2_data + sizeof(clnt_service1_data), sizeof(clnt_service1_data)), 0); END_CALLBACK(VCHIQ_SUCCESS) START_BULK_CALLBACK(VCHIQ_BULK_RECEIVE_ABORTED, 1, 0x1004) END_CALLBACK(VCHIQ_SUCCESS) START_BULK_CALLBACK(VCHIQ_BULK_TRANSMIT_ABORTED, 2, 0x2001) END_CALLBACK(VCHIQ_SUCCESS) } error_exit: callback_count = 0; bulk_count = 0; func_error = 1; vcos_event_signal(&func_test_sync); return VCHIQ_ERROR; } static VCHIQ_STATUS_T fun2_clnt_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T service, void *bulk_userdata) { vcos_unused(header); vcos_unused(service); vcos_unused(bulk_userdata); switch (reason) { case VCHIQ_SERVICE_OPENED: case VCHIQ_SERVICE_CLOSED: case VCHIQ_BULK_TRANSMIT_DONE: break; case VCHIQ_BULK_RECEIVE_DONE: vcos_event_signal(&func_test_sync); break; default: fun2_error = 1; vcos_event_signal(&func_test_sync); break; } return VCHIQ_SUCCESS; } static int mem_check(const void *expected, const void *actual, int size) { if (memcmp(expected, actual, size) != 0) { int i; for (i = 0; i < size; i++) { int ce = ((const char *)expected)[i]; int ca = ((const char *)actual)[i]; if (ca != ce) printf("%08x,%x: %02x <-> %02x\n", i + (unsigned int)actual, i, ce, ca); } printf("mem_check failed - buffer %x, size %d\n", (unsigned int)actual, size); return 1; } return 0; } static void usage(void) { printf("Usage: vchiq_test [] \n"); printf(" where is any of:\n"); printf(" -a set the client and server bulk alignment (modulo 32)\n"); printf(" -A set the client and server bulk alignment (modulo 4096)\n"); printf(" -e disable echoing in the main bulk transfer mode\n"); printf(" -k skip the first func data tests\n"); printf(" -m set the client message quota to \n"); printf(" -M set the server message quota to \n"); printf(" -q disable data verification\n"); printf(" -s ???? service (any 4 characters)\n"); printf(" -v enable more verbose output\n"); printf(" -r reserve bytes for seconds\n"); printf(" -K send a SIGKILL after ms\n"); printf(" and is one of:\n"); printf(" -c control test (size in bytes)\n"); printf(" -b bulk test (size in kilobytes)\n"); printf(" -f functional test\n"); printf(" -p ping test\n"); printf(" -t check the timer\n"); printf(" and is the number of test iterations\n"); exit(1); } static void check_timer(void) { uint32_t start = vcos_getmicrosecs(); uint32_t sleep_time = 1000; printf("0\n"); while (1) { uint32_t now; vcos_sleep(sleep_time); now = vcos_getmicrosecs(); printf("%d - sleep %d\n", now - start, sleep_time); } } static char *buf_align(char *buf, int align_size, int align) { char *aligned = buf - ((intptr_t)buf & (align_size - 1)) + align; if (aligned < buf) aligned += align_size; return aligned; } #ifdef ANDROID static void kill_timeout_handler(int cause, siginfo_t *how, void *ucontext) { printf("Sending signal SIGKILL\n"); kill(main_process_pid, SIGKILL); } static int setup_auto_kill(int timeout_ms) { long timeout; struct timeval interval; if (timeout_ms <= 0) { return -1; } timeout = 1000 * timeout_ms; /* install a signal handler for the alarm */ struct sigaction sa; memset(&sa, 0, sizeof(struct sigaction)); sa.sa_sigaction = kill_timeout_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; if (sigaction(SIGALRM, &sa, 0)) { perror("sigaction"); exit(1); } /* when to expire */ interval.tv_sec = timeout / 1000000; interval.tv_usec = timeout % 1000000; struct itimerval alarm_spec = { .it_interval = {0,0}, .it_value = interval }; int rc = setitimer(ITIMER_REAL, &alarm_spec, NULL); if (rc < 0) { perror("setitimer failed"); exit(1); } return 0; } #endif #ifdef VCOS_APPLICATION_INITIALIZE static VCOS_THREAD_T Task_0; void *main_task(void *param) { vchiq_test(rtos_argc, rtos_argv); VCOS_BKPT; return NULL; } #include "vcfw/logging/logging.h" void VCOS_APPLICATION_INITIALIZE(void *first_available_memory) { const int stack_size = 64*1024; void *pointer = NULL; VCOS_STATUS_T status; logging_init(); logging_level(LOGGING_VCOS); vcos_init(); /* Create task 0. */ #if VCOS_CAN_SET_STACK_ADDR pointer = malloc(stack_size); vcos_demand(pointer); #endif status = vcos_thread_create_classic( &Task_0, "TASK 0", main_task, (void *)0, pointer, stack_size, 10 | VCOS_AFFINITY_DEFAULT, 20, VCOS_START ); vcos_demand(status == VCOS_SUCCESS); } #else int main(int argc, char **argv) { #ifdef ANDROID main_process_pid = getpid(); #endif vcos_init(); vcos_use_android_log = 0; return vchiq_test(argc, argv); } #endif