-/*
-Copyright (C) 2015 Samsung Electronics co., Ltd. All Rights Reserved.
-
-Contact:
- Changyeon Lee <cyeon.lee@samsung.com>,
- JunKyeong Kim <jk0430.kim@samsung.com>,
- Boram Park <boram1288.park@samsung.com>,
- SooChan Lim <sc1.lim@samsung.com>
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice (including the next
-paragraph) shall be included in all copies or substantial portions of the
-Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-*/
+/**************************************************************************
+ *
+ * libtdm
+ *
+ * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
+ *
+ * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
+ * JinYoung Jeon <jy0.jeon@samsung.com>,
+ * Taeheon Kim <th908.kim@samsung.com>,
+ * YoungJun Cho <yj44.cho@samsung.com>,
+ * SooChan Lim <sc1.lim@samsung.com>,
+ * Boram Park <boram1288.park@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+**************************************************************************/
#include <stdio.h>
+#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <errno.h>
#include <time.h>
+#include <stdint.h>
-#include <tdm_client.h>
-#include <tdm_helper.h>
+#include "tdm_client.h"
+#include "tdm_macro.h"
-static int
-get_time_in_millis(void)
+typedef struct _tdm_test_client_arg {
+ char *output_name;
+ int fps;
+ int sync;
+ int interval;
+ int offset;
+ int enable_fake;
+ int pid;
+ char *vblank_name;
+} tdm_test_client_arg;
+
+typedef struct _tdm_test_client {
+ tdm_test_client_arg args;
+
+ int do_query;
+ int do_vblank;
+ int waiting;
+
+ tdm_client *client;
+} tdm_test_client;
+
+struct typestrings {
+ int type;
+ const char *string;
+};
+
+struct optstrings {
+ int type;
+ const char *opt;
+ const char *desc;
+ const char *arg;
+ const char *ex;
+};
+
+enum {
+ OPT_QRY,
+ OPT_TST,
+ OPT_GNR,
+};
+
+static struct typestrings typestrs[] = {
+ {OPT_QRY, "Query"},
+ {OPT_TST, "Test"},
+ {OPT_GNR, "General"},
+};
+
+static struct optstrings optstrs[] = {
+ {OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
+ {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
+};
+
+static void
+usage(char *app_name)
+{
+ int type_size = sizeof(typestrs) / sizeof(struct typestrings);
+ int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
+ int t;
+
+ printf("usage: %s \n\n", app_name);
+
+ for (t = 0; t < type_size; t++) {
+ int o, f = 1;
+
+ for (o = 0; o < opt_size; o++)
+ if (optstrs[o].type == typestrs[t].type) {
+ if (f == 1)
+ printf(" %s options:\n\n", typestrs[t].string);
+ printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
+ if (optstrs[o].arg)
+ printf("\t\t %s\n", optstrs[o].arg);
+ if (optstrs[o].ex)
+ printf("\t\t ex) %s\n", optstrs[o].ex);
+ f = 0;
+ }
+ printf("\n");
+ }
+
+ exit(0);
+}
+
+//"<output_name>"
+static void
+parse_arg_qo(tdm_test_client *data, char *arg)
+{
+ char name[TDM_NAME_LEN];
+ strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.output_name = strndup(name, TDM_NAME_LEN);
+}
+
+//"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
+static void
+parse_arg_v(tdm_test_client *data, char *arg)
+{
+ char *end = arg;
+ char name[TDM_NAME_LEN];
+
+ end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.output_name = strndup(name, TDM_NAME_LEN);
+
+ if (*end == ',') {
+ arg = end + 1;
+ data->args.sync = strtol(arg, &end, 10);
+ }
+
+ if (*end == '@') {
+ arg = end + 1;
+ data->args.fps = strtol(arg, &end, 10);
+ }
+
+ if (*end == '~') {
+ arg = end + 1;
+ data->args.interval = strtol(arg, &end, 10);
+ }
+
+ if (*end == '+' || *end == '-') {
+ arg = end;
+ data->args.offset = strtol(arg, &end, 10);
+ }
+
+ if (*end == '*') {
+ arg = end + 1;
+ data->args.enable_fake = strtol(arg, &end, 10);
+ }
+
+ if (*end == '^') {
+ char name[TDM_NAME_LEN];
+ arg = end + 1;
+ end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
+ data->args.vblank_name = strndup(name, TDM_NAME_LEN);
+ }
+}
+
+static void
+parse_args(tdm_test_client *data, int argc, char *argv[])
+{
+ int i;
+
+ if (argc < 3) {
+ usage(argv[0]);
+ exit(0);
+ }
+
+ memset(data, 0, sizeof *data);
+ data->args.interval = 1;
+
+ for (i = 1; i < argc; i++) {
+ if (!strncmp(argv[i] + 1, "qo", 2)) {
+ data->do_query = 1;
+ parse_arg_qo(data, argv[++i]);
+ } else if (!strncmp(argv[i] + 1, "v", 1)) {
+ data->do_vblank = 1;
+ parse_arg_v(data, argv[++i]);
+ } else {
+ usage(argv[0]);
+ exit(0);
+ }
+ }
+}
+
+static double
+get_time(void)
{
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
- return (int)(tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
+ return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
return 0;
}
static void
-_client_vblank_handler(unsigned int sequence, unsigned int tv_sec,
- unsigned int tv_usec, void *user_data)
+_client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec, void *user_data)
{
- int temp1, temp2;
+ tdm_test_client *data = user_data;
+ double cur, vbl;
+ static double p_vbl = 0;
- temp1 = (int)user_data;
- temp2 = get_time_in_millis();
+ data->waiting = 0;
- printf("%d ms\n", temp2 - temp1);
+ if (error == TDM_ERROR_DPMS_OFF) {
+ printf("exit: dpms off\n");
+ exit(0);
+ }
+
+ if (error != TDM_ERROR_NONE) {
+ printf("exit: error(%d)\n", error);
+ exit(0);
+ }
+
+ cur = get_time();
+ vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
+
+ printf("vblank : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
+
+ if (cur - vbl > 0.002) /* 2ms */
+ printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
+
+ p_vbl = vbl;
}
+static char *conn_str[3] = {"disconnected", "connected", "mode_setted"};
+static char *dpms_str[4] = {"on", "standy", "suspend", "off"};
-int
-main(int argc, char *argv[])
+static void
+_client_output_handler(tdm_client_output *output, tdm_output_change_type type,
+ tdm_value value, void *user_data)
{
- tdm_client *client;
- tdm_client_error error;
+ if (type == TDM_OUTPUT_CHANGE_CONNECTION)
+ printf("output %s.\n", conn_str[value.u32]);
+ else if (type == TDM_OUTPUT_CHANGE_DPMS)
+ printf("dpms %s.\n", dpms_str[value.u32]);
+}
+
+static void
+do_query(tdm_test_client *data)
+{
+ tdm_client_output *output;
+ tdm_output_conn_status status;
+ tdm_output_dpms dpms;
+ unsigned int refresh;
+ tdm_error error;
+
+ output = tdm_client_get_output(data->client, NULL, &error);
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_get_output failed\n");
+ return;
+ }
+
+ error = tdm_client_output_get_conn_status(output, &status);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_output_get_dpms(output, &dpms);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_output_get_refresh_rate(output, &refresh);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+
+ printf("tdm_output \"%s\"\n", data->args.output_name);
+ printf("\tstatus : %s\n", conn_str[status]);
+ printf("\tdpms : %s\n", dpms_str[dpms]);
+ printf("\trefresh : %d\n", refresh);
+}
+
+static void
+do_vblank(tdm_test_client *data)
+{
+ tdm_client_output *output;
+ tdm_client_vblank *vblank = NULL;
+ tdm_error error;
int fd = -1;
struct pollfd fds;
- client = tdm_client_create(&error);
- if (error != TDM_CLIENT_ERROR_NONE) {
- printf("tdm_client_create failed\n");
- exit(1);
+ output = tdm_client_get_output(data->client, data->args.output_name, &error);
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_get_output failed\n");
+ return;
+ }
+
+ error = tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+
+ vblank = tdm_client_output_create_vblank(output, &error);
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_output_create_vblank failed\n");
+ return;
}
- error = tdm_client_get_fd(client, &fd);
- if (error != TDM_CLIENT_ERROR_NONE || fd < 0) {
+ error = tdm_client_vblank_set_name(vblank, data->args.vblank_name);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ error = tdm_client_vblank_set_sync(vblank, data->args.sync);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ if (data->args.fps > 0) {
+ error = tdm_client_vblank_set_fps(vblank, data->args.fps);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+ }
+ error = tdm_client_vblank_set_offset(vblank, data->args.offset);
+ TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
+
+ error = tdm_client_get_fd(data->client, &fd);
+ if (error != TDM_ERROR_NONE || fd < 0) {
printf("tdm_client_get_fd failed\n");
goto done;
}
while (1) {
int ret;
- error = tdm_client_wait_vblank(client, "unknown-0", 1, 0,
- _client_vblank_handler,
- (void*)get_time_in_millis());
- if (error != TDM_CLIENT_ERROR_NONE) {
- printf("tdm_client_wait_vblank failed\n");
- goto done;
+ if (!data->waiting) {
+ error = tdm_client_vblank_wait(vblank, data->args.interval,
+ _client_vblank_handler, data);
+ if (error == TDM_ERROR_DPMS_OFF) {
+ printf("tdm_client_vblank_wait failed (dpms off)\n");
+ goto done;
+ }
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_vblank_wait failed (error: %d)\n", error);
+ goto done;
+ }
+ data->waiting = 1;
}
- ret = poll(&fds, 1, -1);
- if (ret < 0) {
- if (errno == EBUSY) /* normal case */
- continue;
- else {
- printf("poll failed: %m\n");
+ if (!data->args.sync) {
+ ret = poll(&fds, 1, -1);
+ if (ret < 0) {
+ if (errno == EINTR || errno == EAGAIN) /* normal case */
+ continue;
+ else {
+ printf("poll failed: %m\n");
+ goto done;
+ }
+ }
+
+ error = tdm_client_handle_events(data->client);
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_handle_events failed\n");
goto done;
}
}
+ }
+
+done:
+ if (vblank)
+ tdm_client_vblank_destroy(vblank);
+}
+
+static tdm_test_client ttc_data;
- error = tdm_client_handle_events(client);
- if (error != TDM_CLIENT_ERROR_NONE)
- printf("tdm_client_handle_events failed\n");
+int
+main(int argc, char *argv[])
+{
+ tdm_test_client *data = &ttc_data;
+ tdm_error error;
+
+#if 1 /* for testing */
+ const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
+ if (!xdg) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "/run");
+ int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
+ if (ret != 0)
+ exit(0);
+ }
+#endif
+
+ parse_args(data, argc, argv);
+
+ printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
+ data->args.sync, data->args.fps, data->args.interval,
+ data->args.offset, data->args.enable_fake, data->args.pid);
+
+ data->client = tdm_client_create(&error);
+ if (error != TDM_ERROR_NONE) {
+ printf("tdm_client_create failed\n");
+ goto done;
}
+ if (data->do_query)
+ do_query(data);
+ if (data->do_vblank)
+ do_vblank(data);
+
done:
- tdm_client_destroy(client);
+ if (data->args.output_name)
+ free(data->args.output_name);
+ if (data->args.vblank_name)
+ free(data->args.vblank_name);
+ if (data->client)
+ tdm_client_destroy(data->client);
+
return 0;
}