vblank: add set_vblank_fps functionality for the given PID
[platform/core/uifw/libtdm.git] / tools / tdm_test_client.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <sc1.lim@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <poll.h>
40 #include <errno.h>
41 #include <time.h>
42 #include <stdint.h>
43
44 #include <tdm_client.h>
45 #include "tdm_macro.h"
46
47 typedef struct _tdm_test_client_arg {
48         char *output_name;
49         int fps;
50         int sync;
51         int interval;
52         int offset;
53         int enable_fake;
54         int pid;
55         char *vblank_name;
56 } tdm_test_client_arg;
57
58 typedef struct _tdm_test_client {
59         tdm_test_client_arg args;
60
61         int do_query;
62         int do_vblank;
63         int do_set_fps;
64         int waiting;
65
66         tdm_client *client;
67 } tdm_test_client;
68
69 struct typestrings {
70         int type;
71         const char *string;
72 };
73
74 struct optstrings {
75         int  type;
76         const char *opt;
77         const char *desc;
78         const char *arg;
79         const char *ex;
80 };
81
82 enum {
83         OPT_QRY,
84         OPT_TST,
85         OPT_GNR,
86 };
87
88 static struct typestrings typestrs[] = {
89         {OPT_QRY, "Query"},
90         {OPT_TST, "Test"},
91         {OPT_GNR, "General"},
92 };
93
94 static struct optstrings optstrs[] = {
95         {OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
96         {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
97         {OPT_TST, "f", "fps setting test", "<pid>[,<vblank_name>]@<fps>", "<pid>@60"},
98 };
99
100 static void
101 usage(char *app_name)
102 {
103         int type_size = sizeof(typestrs) / sizeof(struct typestrings);
104         int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
105         int t;
106
107         printf("usage: %s \n\n", app_name);
108
109         for (t = 0; t < type_size; t++) {
110                 int o, f = 1;
111
112                 for (o = 0; o < opt_size; o++)
113                         if (optstrs[o].type == typestrs[t].type) {
114                                 if (f == 1)
115                                         printf(" %s options:\n\n", typestrs[t].string);
116                                 printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
117                                 if (optstrs[o].arg)
118                                         printf("\t\t  %s\n", optstrs[o].arg);
119                                 if (optstrs[o].ex)
120                                         printf("\t\t  ex) %s\n", optstrs[o].ex);
121                                 f = 0;
122                         }
123                 printf("\n");
124         }
125
126         exit(0);
127 }
128
129 //"<output_name>"
130 static void
131 parse_arg_qo(tdm_test_client *data, char *arg)
132 {
133         char name[TDM_NAME_LEN];
134         strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
135         data->args.output_name = strndup(name, TDM_NAME_LEN);
136 }
137
138 //"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
139 static void
140 parse_arg_v(tdm_test_client *data, char *arg)
141 {
142         char *end = arg;
143         char name[TDM_NAME_LEN];
144
145         end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
146         data->args.output_name = strndup(name, TDM_NAME_LEN);
147
148         if (*end == ',') {
149                 arg = end + 1;
150                 data->args.sync = strtol(arg, &end, 10);
151         }
152
153         if (*end == '@') {
154                 arg = end + 1;
155                 data->args.fps = strtol(arg, &end, 10);
156         }
157
158         if (*end == '~') {
159                 arg = end + 1;
160                 data->args.interval = strtol(arg, &end, 10);
161         }
162
163         if (*end == '+' || *end == '-') {
164                 arg = end;
165                 data->args.offset = strtol(arg, &end, 10);
166         }
167
168         if (*end == '*') {
169                 arg = end + 1;
170                 data->args.enable_fake = strtol(arg, &end, 10);
171         }
172
173         if (*end == '^') {
174                 char name[TDM_NAME_LEN];
175                 arg = end + 1;
176                 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
177                 data->args.vblank_name = strndup(name, TDM_NAME_LEN);
178         }
179 }
180
181 //"<pid>@<fps>"
182 static void
183 parse_arg_f(tdm_test_client *data, char *arg)
184 {
185         char *end = arg;
186
187         data->args.pid = strtol(arg, &end, 10);
188
189         if (*end == ',') {
190                 char name[TDM_NAME_LEN];
191                 arg = end + 1;
192                 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
193                 data->args.vblank_name = strndup(name, TDM_NAME_LEN);
194         }
195
196         if (*end != '@') {
197                 printf("failed: no fps value\n");
198                 exit(0);
199         }
200
201         arg = end + 1;
202         data->args.fps = strtol(arg, &end, 10);
203 }
204
205 static void
206 parse_args(tdm_test_client *data, int argc, char *argv[])
207 {
208         int i;
209
210         if (argc < 3) {
211                 usage(argv[0]);
212                 exit(0);
213         }
214
215         memset(data, 0, sizeof *data);
216         data->args.interval = 1;
217
218         for (i = 1; i < argc; i++) {
219                 if (!strncmp(argv[i] + 1, "qo", 2)) {
220                         data->do_query = 1;
221                         parse_arg_qo(data, argv[++i]);
222                 } else if (!strncmp(argv[i] + 1, "v", 1)) {
223                         data->do_vblank = 1;
224                         parse_arg_v(data, argv[++i]);
225                 } else if (!strncmp(argv[i] + 1, "f", 1)) {
226                         data->do_set_fps = 1;
227                         parse_arg_f(data, argv[++i]);
228                 } else {
229                         usage(argv[0]);
230                         exit(0);
231                 }
232         }
233 }
234
235 static double
236 get_time(void)
237 {
238         struct timespec tp;
239
240         if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
241                 return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
242
243         return 0;
244 }
245
246 static void
247 _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
248                                            unsigned int tv_sec, unsigned int tv_usec, void *user_data)
249 {
250         tdm_test_client *data = user_data;
251         double cur, vbl;
252         static double p_vbl = 0;
253
254         data->waiting = 0;
255
256         if (error == TDM_ERROR_DPMS_OFF) {
257                 printf("exit: dpms off\n");
258                 exit(0);
259         }
260
261         if (error != TDM_ERROR_NONE) {
262                 printf("exit: error(%d)\n", error);
263                 exit(0);
264         }
265
266         cur = get_time();
267         vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
268
269         printf("vblank              : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
270
271         if (cur - vbl > 0.002) /* 2ms */
272                 printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
273
274         p_vbl = vbl;
275 }
276
277 static char *conn_str[3] = {"disconnected", "connected", "mode_setted"};
278 static char *dpms_str[4] = {"on", "standy", "suspend", "off"};
279
280 static void
281 _client_output_handler(tdm_client_output *output, tdm_output_change_type type,
282                                            tdm_value value, void *user_data)
283 {
284         if (type == TDM_OUTPUT_CHANGE_CONNECTION)
285                 printf("output %s.\n", conn_str[value.u32]);
286         else if (type == TDM_OUTPUT_CHANGE_DPMS)
287                 printf("dpms %s.\n", dpms_str[value.u32]);
288 }
289
290 static void
291 do_query(tdm_test_client *data)
292 {
293         tdm_client_output *output;
294         tdm_output_conn_status status;
295         tdm_output_dpms dpms;
296         unsigned int refresh;
297         tdm_error error;
298
299         output = tdm_client_get_output(data->client, NULL, &error);
300         if (error != TDM_ERROR_NONE) {
301                 printf("tdm_client_get_output failed\n");
302                 return;
303         }
304
305         tdm_client_output_get_conn_status(output, &status);
306         tdm_client_output_get_dpms(output, &dpms);
307         tdm_client_output_get_refresh_rate(output, &refresh);
308
309         printf("tdm_output \"%s\"\n", data->args.output_name);
310         printf("\tstatus : %s\n", conn_str[status]);
311         printf("\tdpms : %s\n", dpms_str[dpms]);
312         printf("\trefresh : %d\n", refresh);
313 }
314
315 static void
316 do_vblank(tdm_test_client *data)
317 {
318         tdm_client_output *output;
319         tdm_client_vblank *vblank = NULL;
320         tdm_error error;
321         int fd = -1;
322         struct pollfd fds;
323
324         output = tdm_client_get_output(data->client, data->args.output_name, &error);
325         if (error != TDM_ERROR_NONE) {
326                 printf("tdm_client_get_output failed\n");
327                 return;
328         }
329
330         tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
331
332         vblank = tdm_client_output_create_vblank(output, &error);
333         if (error != TDM_ERROR_NONE) {
334                 printf("tdm_client_output_create_vblank failed\n");
335                 return;
336         }
337
338         tdm_client_vblank_set_name(vblank, data->args.vblank_name);
339         tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
340         tdm_client_vblank_set_sync(vblank, data->args.sync);
341         if (data->args.fps > 0)
342                 tdm_client_vblank_set_fps(vblank, data->args.fps);
343         tdm_client_vblank_set_offset(vblank, data->args.offset);
344
345         error = tdm_client_get_fd(data->client, &fd);
346         if (error != TDM_ERROR_NONE || fd < 0) {
347                 printf("tdm_client_get_fd failed\n");
348                 goto done;
349         }
350
351         fds.events = POLLIN;
352         fds.fd = fd;
353         fds.revents = 0;
354
355         while (1) {
356                 int ret;
357
358                 if (!data->waiting) {
359                         error = tdm_client_vblank_wait(vblank, data->args.interval,
360                                                                                    _client_vblank_handler, data);
361                         if (error == TDM_ERROR_DPMS_OFF) {
362                                 printf("tdm_client_vblank_wait failed (dpms off)\n");
363                                 goto done;
364                         }
365                         if (error != TDM_ERROR_NONE) {
366                                 printf("tdm_client_vblank_wait failed (error: %d)\n", error);
367                                 goto done;
368                         }
369                         data->waiting = 1;
370                 }
371
372                 if (!data->args.sync) {
373                         ret = poll(&fds, 1, -1);
374                         if (ret < 0) {
375                                 if (errno == EINTR || errno == EAGAIN)  /* normal case */
376                                         continue;
377                                 else {
378                                         printf("poll failed: %m\n");
379                                         goto done;
380                                 }
381                         }
382
383                         error = tdm_client_handle_events(data->client);
384                         if (error != TDM_ERROR_NONE) {
385                                 printf("tdm_client_handle_events failed\n");
386                                 goto done;
387                         }
388                 }
389         }
390
391 done:
392         if (vblank)
393                 tdm_client_vblank_destroy(vblank);
394 }
395
396 static void
397 do_set_fps(tdm_test_client *data)
398 {
399         tdm_error error;
400
401         error = tdm_client_set_client_vblank_fps(data->client, data->args.pid, data->args.vblank_name, data->args.fps);
402         if (error != TDM_ERROR_NONE) {
403                 printf("tdm_client_set_client_vblank_fps failed\n");
404                 return;
405         }
406 }
407
408 static tdm_test_client ttc_data;
409
410 int
411 main(int argc, char *argv[])
412 {
413         tdm_test_client *data = &ttc_data;
414         tdm_error error;
415
416 #if 1 /* for testing */
417         const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
418         if (!xdg) {
419                 char buf[32];
420                 snprintf(buf, sizeof(buf), "/run");
421                 int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
422                 if (ret != 0)
423                         exit(0);
424         }
425 #endif
426
427         parse_args(data, argc, argv);
428
429         printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
430                    data->args.sync, data->args.fps, data->args.interval,
431                    data->args.offset, data->args.enable_fake, data->args.pid);
432
433         data->client = tdm_client_create(&error);
434         if (error != TDM_ERROR_NONE) {
435                 printf("tdm_client_create failed\n");
436                 goto done;
437         }
438
439         if (data->do_query)
440                 do_query(data);
441         if (data->do_vblank)
442                 do_vblank(data);
443         if (data->do_set_fps)
444                 do_set_fps(data);
445
446 done:
447         if (data->args.output_name)
448                 free(data->args.output_name);
449         if (data->args.vblank_name)
450                 free(data->args.vblank_name);
451         if (data->client)
452                 tdm_client_destroy(data->client);
453
454         return 0;
455 }