virtual: add tdm virtual output test to tdm_test with V option
[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 <boram1288.park@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 #include <png.h>
44
45 #include "tdm_client.h"
46 #include "tdm_macro.h"
47 #include "buffers.h"
48
49 typedef struct _tdm_test_client_arg {
50         char *output_name;
51         int fps;
52         int sync;
53         int interval;
54         int offset;
55         int enable_fake;
56         int pid;
57         char *vblank_name;
58 } tdm_test_client_arg;
59
60 typedef struct _tdm_test_client {
61         tdm_test_client_arg args;
62
63         int do_query;
64         int do_vblank;
65         int do_voutput;
66         int waiting;
67
68         tdm_client *client;
69 } tdm_test_client;
70
71 struct typestrings {
72         int type;
73         const char *string;
74 };
75
76 struct optstrings {
77         int  type;
78         const char *opt;
79         const char *desc;
80         const char *arg;
81         const char *ex;
82 };
83
84 enum {
85         OPT_QRY,
86         OPT_TST,
87         OPT_GNR,
88 };
89
90 static struct typestrings typestrs[] = {
91         {OPT_QRY, "Query"},
92         {OPT_TST, "Test"},
93         {OPT_GNR, "General"},
94 };
95
96 static struct optstrings optstrs[] = {
97         {OPT_QRY, "qo", "output objects info", "<output_name>", "primary"},
98         {OPT_TST, "v", "vblank test", "<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake][^vblank_name]", "primary,0@60~1+0*1^test"},
99         {OPT_TST, "V", "virtual output test", NULL, NULL},
100 };
101
102 static void
103 usage(char *app_name)
104 {
105         int type_size = sizeof(typestrs) / sizeof(struct typestrings);
106         int opt_size = sizeof(optstrs) / sizeof(struct optstrings);
107         int t;
108
109         printf("usage: %s \n\n", app_name);
110
111         for (t = 0; t < type_size; t++) {
112                 int o, f = 1;
113
114                 for (o = 0; o < opt_size; o++)
115                         if (optstrs[o].type == typestrs[t].type) {
116                                 if (f == 1)
117                                         printf(" %s options:\n\n", typestrs[t].string);
118                                 printf("\t-%s\t%s\n", optstrs[o].opt, optstrs[o].desc);
119                                 if (optstrs[o].arg)
120                                         printf("\t\t  %s\n", optstrs[o].arg);
121                                 if (optstrs[o].ex)
122                                         printf("\t\t  ex) %s\n", optstrs[o].ex);
123                                 f = 0;
124                         }
125                 printf("\n");
126         }
127
128         exit(0);
129 }
130
131 //"<output_name>"
132 static void
133 parse_arg_qo(tdm_test_client *data, char *arg)
134 {
135         char name[TDM_NAME_LEN];
136         strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
137         data->args.output_name = strndup(name, TDM_NAME_LEN);
138 }
139
140 //"<output_name>[,<sync>][@<fps>][~<interval>][+<offset>][*fake]"
141 static void
142 parse_arg_v(tdm_test_client *data, char *arg)
143 {
144         char *end = arg;
145         char name[TDM_NAME_LEN];
146
147         end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
148         data->args.output_name = strndup(name, TDM_NAME_LEN);
149
150         if (*end == ',') {
151                 arg = end + 1;
152                 data->args.sync = strtol(arg, &end, 10);
153         }
154
155         if (*end == '@') {
156                 arg = end + 1;
157                 data->args.fps = strtol(arg, &end, 10);
158         }
159
160         if (*end == '~') {
161                 arg = end + 1;
162                 data->args.interval = strtol(arg, &end, 10);
163         }
164
165         if (*end == '+' || *end == '-') {
166                 arg = end;
167                 data->args.offset = strtol(arg, &end, 10);
168         }
169
170         if (*end == '*') {
171                 arg = end + 1;
172                 data->args.enable_fake = strtol(arg, &end, 10);
173         }
174
175         if (*end == '^') {
176                 char name[TDM_NAME_LEN];
177                 arg = end + 1;
178                 end = strtostr(name, TDM_NAME_LEN, arg, TDM_DELIM);
179                 data->args.vblank_name = strndup(name, TDM_NAME_LEN);
180         }
181 }
182
183 static void
184 parse_args(tdm_test_client *data, int argc, char *argv[])
185 {
186         int i;
187
188         if (argc < 2) {
189                 usage(argv[0]);
190                 exit(0);
191         }
192
193         memset(data, 0, sizeof *data);
194         data->args.interval = 1;
195
196         for (i = 1; i < argc; i++) {
197                 if (!strncmp(argv[i] + 1, "qo", 2)) {
198                         data->do_query = 1;
199                         parse_arg_qo(data, argv[++i]);
200                 } else if (!strncmp(argv[i] + 1, "v", 1)) {
201                         data->do_vblank = 1;
202                         parse_arg_v(data, argv[++i]);
203                 } else if (!strncmp(argv[i] + 1, "V", 1)) {
204                         data->do_voutput = 1;
205                 } else {
206                         usage(argv[0]);
207                         exit(0);
208                 }
209         }
210 }
211
212 static double
213 get_time(void)
214 {
215         struct timespec tp;
216
217         if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
218                 return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
219
220         return 0;
221 }
222
223 static void
224 _client_vblank_handler(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
225                                            unsigned int tv_sec, unsigned int tv_usec, void *user_data)
226 {
227         tdm_test_client *data = user_data;
228         double cur, vbl;
229         static double p_vbl = 0;
230
231         data->waiting = 0;
232
233         if (error == TDM_ERROR_DPMS_OFF) {
234                 printf("exit: dpms off\n");
235                 exit(0);
236         }
237
238         if (error != TDM_ERROR_NONE) {
239                 printf("exit: error(%d)\n", error);
240                 exit(0);
241         }
242
243         cur = get_time();
244         vbl = (double)tv_sec + ((double)tv_usec) / 1000000.0;
245
246         printf("vblank              : %.6f us vbl(%.6f)\n", vbl - p_vbl, vbl);
247
248         if (cur - vbl > 0.002) /* 2ms */
249                 printf("kernel -> tdm-client: %.0f us\n", (cur - vbl) * 1000000.0);
250
251         p_vbl = vbl;
252 }
253
254 static char *conn_str[3] = {"disconnected", "connected", "mode_setted"};
255 static char *dpms_str[4] = {"on", "standy", "suspend", "off"};
256
257 static void
258 _client_output_handler(tdm_client_output *output, tdm_output_change_type type,
259                                            tdm_value value, void *user_data)
260 {
261         if (type == TDM_OUTPUT_CHANGE_CONNECTION)
262                 printf("output %s.\n", conn_str[value.u32]);
263         else if (type == TDM_OUTPUT_CHANGE_DPMS)
264                 printf("dpms %s.\n", dpms_str[value.u32]);
265 }
266
267 static void
268 do_query(tdm_test_client *data)
269 {
270         tdm_client_output *output;
271         tdm_output_conn_status status;
272         tdm_output_dpms dpms;
273         unsigned int refresh;
274         tdm_error error;
275
276         output = tdm_client_get_output(data->client, NULL, &error);
277         if (error != TDM_ERROR_NONE) {
278                 printf("tdm_client_get_output failed\n");
279                 return;
280         }
281
282         error = tdm_client_output_get_conn_status(output, &status);
283         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
284         error = tdm_client_output_get_dpms(output, &dpms);
285         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
286         error = tdm_client_output_get_refresh_rate(output, &refresh);
287         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
288
289         printf("tdm_output \"%s\"\n", data->args.output_name);
290         printf("\tstatus : %s\n", conn_str[status]);
291         printf("\tdpms : %s\n", dpms_str[dpms]);
292         printf("\trefresh : %d\n", refresh);
293 }
294
295 static void
296 do_vblank(tdm_test_client *data)
297 {
298         tdm_client_output *output;
299         tdm_client_vblank *vblank = NULL;
300         tdm_error error;
301         int fd = -1;
302         struct pollfd fds;
303
304         output = tdm_client_get_output(data->client, data->args.output_name, &error);
305         if (error != TDM_ERROR_NONE) {
306                 printf("tdm_client_get_output failed\n");
307                 return;
308         }
309
310         error = tdm_client_output_add_change_handler(output, _client_output_handler, NULL);
311         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
312
313         vblank = tdm_client_output_create_vblank(output, &error);
314         if (error != TDM_ERROR_NONE) {
315                 printf("tdm_client_output_create_vblank failed\n");
316                 return;
317         }
318
319         error = tdm_client_vblank_set_name(vblank, data->args.vblank_name);
320         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
321         error = tdm_client_vblank_set_enable_fake(vblank, data->args.enable_fake);
322         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
323         error = tdm_client_vblank_set_sync(vblank, data->args.sync);
324         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
325         if (data->args.fps > 0) {
326                 error = tdm_client_vblank_set_fps(vblank, data->args.fps);
327                 TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
328         }
329         error = tdm_client_vblank_set_offset(vblank, data->args.offset);
330         TDM_WARNING_IF_FAIL(error == TDM_ERROR_NONE);
331
332         error = tdm_client_get_fd(data->client, &fd);
333         if (error != TDM_ERROR_NONE || fd < 0) {
334                 printf("tdm_client_get_fd failed\n");
335                 goto done;
336         }
337
338         fds.events = POLLIN;
339         fds.fd = fd;
340         fds.revents = 0;
341
342         while (1) {
343                 int ret;
344
345                 if (!data->waiting) {
346                         error = tdm_client_vblank_wait(vblank, data->args.interval,
347                                                                                    _client_vblank_handler, data);
348                         if (error == TDM_ERROR_DPMS_OFF) {
349                                 printf("tdm_client_vblank_wait failed (dpms off)\n");
350                                 goto done;
351                         }
352                         if (error != TDM_ERROR_NONE) {
353                                 printf("tdm_client_vblank_wait failed (error: %d)\n", error);
354                                 goto done;
355                         }
356                         data->waiting = 1;
357                 }
358
359                 if (!data->args.sync) {
360                         ret = poll(&fds, 1, -1);
361                         if (ret < 0) {
362                                 if (errno == EINTR || errno == EAGAIN)  /* normal case */
363                                         continue;
364                                 else {
365                                         printf("poll failed: %m\n");
366                                         goto done;
367                                 }
368                         }
369
370                         error = tdm_client_handle_events(data->client);
371                         if (error != TDM_ERROR_NONE) {
372                                 printf("tdm_client_handle_events failed\n");
373                                 goto done;
374                         }
375                 }
376         }
377
378 done:
379         if (vblank)
380                 tdm_client_vblank_destroy(vblank);
381 }
382
383 #define PNG_DEPTH 8
384
385 void
386 _tdm_client_get_buffer_full_size(tbm_surface_h buffer, int *buffer_w, int *buffer_h)
387 {
388         tbm_surface_info_s info;
389         int ret;
390
391         TDM_RETURN_IF_FAIL(buffer != NULL);
392
393         ret = tbm_surface_get_info(buffer, &info);
394         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
395
396         if (buffer_w) {
397                 if (IS_RGB(info.format))
398                         *buffer_w = info.planes[0].stride >> 2;
399                 else
400                         *buffer_w = info.planes[0].stride;
401         }
402
403         if (buffer_h)
404                 *buffer_h = info.planes[0].size / info.planes[0].stride;
405 }
406
407 static void
408 _tdm_client_dump_png(const char *file, const void *data, int width,
409                                          int height)
410 {
411         FILE *fp;
412
413         fp = fopen(file, "wb");
414         TDM_RETURN_IF_FAIL(fp != NULL);
415
416         png_structp pPngStruct =
417                 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
418         if (!pPngStruct) {
419                 fclose(fp);
420                 return;
421         }
422
423         png_infop pPngInfo = png_create_info_struct(pPngStruct);
424         if (!pPngInfo) {
425                 png_destroy_write_struct(&pPngStruct, NULL);
426                 fclose(fp);
427                 return;
428         }
429
430         png_init_io(pPngStruct, fp);
431         png_set_IHDR(pPngStruct,
432                                  pPngInfo,
433                                  width,
434                                  height,
435                                  PNG_DEPTH,
436                                  PNG_COLOR_TYPE_RGBA,
437                                  PNG_INTERLACE_NONE,
438                                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
439
440         png_set_bgr(pPngStruct);
441         png_write_info(pPngStruct, pPngInfo);
442
443         const int pixel_size = 4;       // RGBA
444         png_bytep *row_pointers =
445                 png_malloc(pPngStruct, height * sizeof(png_byte *));
446         if (!row_pointers) {
447                 png_destroy_write_struct(&pPngStruct, &pPngInfo);
448                 fclose(fp);
449                 return;
450         }
451
452         unsigned int *blocks = (unsigned int *)data;
453         int y = 0;
454         int x = 0;
455
456         for (; y < height; ++y) {
457                 png_bytep row =
458                         png_malloc(pPngStruct, sizeof(png_byte) * width * pixel_size);
459                 if (!row) {
460                         for (x = 0; x < y; x++)
461                                 png_free(pPngStruct, row_pointers[x]);
462                         png_free(pPngStruct, row_pointers);
463                         png_destroy_write_struct(&pPngStruct, &pPngInfo);
464                         fclose(fp);
465                         return;
466                 }
467
468                 row_pointers[y] = (png_bytep)row;
469                 for (x = 0; x < width; ++x) {
470                         unsigned int curBlock = blocks[y * width + x];
471                         row[x * pixel_size] = (curBlock & 0xFF);
472                         row[1 + x * pixel_size] = (curBlock >> 8) & 0xFF;
473                         row[2 + x * pixel_size] = (curBlock >> 16) & 0xFF;
474                         row[3 + x * pixel_size] = (curBlock >> 24) & 0xFF;
475                 }
476         }
477
478         png_write_image(pPngStruct, row_pointers);
479         png_write_end(pPngStruct, pPngInfo);
480
481         for (y = 0; y < height; y++)
482                 png_free(pPngStruct, row_pointers[y]);
483         png_free(pPngStruct, row_pointers);
484
485         png_destroy_write_struct(&pPngStruct, &pPngInfo);
486
487         fclose(fp);
488 }
489
490 void
491 _tdm_client_dump_buffer(tbm_surface_h buffer, const char *file)
492 {
493         char temp[TDM_PATH_LEN] = {0,};
494         tbm_surface_info_s info;
495         int len, ret;
496         const char *ext;
497         int bo_cnt;
498         int bw, bh;
499         char *dot, *p = temp;
500         const char *file_exts[2] = {"png", "raw"};
501
502         TDM_RETURN_IF_FAIL(buffer != NULL);
503         TDM_RETURN_IF_FAIL(file != NULL);
504
505         ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info);
506         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
507
508         if (IS_RGB(info.format))
509                 ext = file_exts[0];
510         else
511                 ext = file_exts[1];
512
513         dot = strrchr(file, '.');
514         if (!dot || strlen(dot + 1) != 3 || strncmp(dot + 1, ext, 3)) {
515                 len = strnlen(file, TDM_PATH_LEN - 5);
516                 strncat(p, file, len);
517                 p += len;
518                 *(p++) = '.';
519                 strncat(p, ext, 3);
520                 p += 3;
521                 *p = '\0';
522         } else {
523                 len = strnlen(file, TDM_PATH_LEN - 1);
524                 strncat(p, file, len);
525                 p += len;
526                 *p = '\0';
527         }
528
529         _tdm_client_get_buffer_full_size(buffer, &bw, &bh);
530
531         bo_cnt = tbm_surface_internal_get_num_bos(buffer);
532         TDM_DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)",
533                         bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format),
534                         info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size,
535                         info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size,
536                         info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size);
537
538         _tdm_client_dump_png(temp, info.planes[0].ptr, bw, bh);
539
540         tbm_surface_unmap(buffer);
541
542         printf("dump %s", temp);
543 }
544
545 static void
546 _dump_buffer(tbm_surface_h buffer, int count)
547 {
548         char temp[TDM_PATH_LEN] = {0,};
549
550         snprintf(temp, TDM_PATH_LEN, "/tmp/%c%c%c%c_%dx%d_%d",
551                 FOURCC_STR(tbm_surface_get_format(buffer)),
552                 tbm_surface_get_width(buffer),
553                 tbm_surface_get_height(buffer),
554                 count);
555         _tdm_client_dump_buffer(buffer, temp);
556 }
557
558 static void
559 _voutput_commit(tdm_client_voutput *voutput, tbm_surface_h buffer, void *user_data)
560 {
561         tdm_test_client *data = (tdm_test_client *)user_data;
562         static int count = 0;
563
564         TDM_EXIT_IF_FAIL(data != NULL);
565         TDM_EXIT_IF_FAIL(buffer != NULL);
566
567         if (count < 11)
568                 _dump_buffer(buffer, count);
569         count++;
570
571         printf("client: %d commited(%p)\n", count, buffer);
572         tdm_client_voutput_commit_done(voutput);
573 }
574
575  static void
576 _voutput_make_available_mode(tdm_client_output_mode *modes, int count)
577 {
578         int i;
579         for (i = 0; i < count; i++) {
580                 modes[i].clock = 25200;
581                 modes[i].hdisplay = 640;
582                 modes[i].hsync_start = 656;
583                 modes[i].hsync_end = 752;
584                 modes[i].htotal = 800;
585                 modes[i].hskew = 0;
586                 modes[i].vdisplay = 480;
587                 modes[i].vsync_start = 490;
588                 modes[i].vsync_end = 492;
589                 modes[i].vtotal = 525;
590                 modes[i].vscan = 0;
591                 modes[i].vrefresh = 30;
592                 modes[i].flags = 0;
593                 modes[i].type = 0;
594                 snprintf(modes[i].name, TDM_NAME_LEN, "%dx%d_%d", modes[i].hdisplay, modes[i].vdisplay, i);
595         }
596 }
597
598 static void
599 do_voutput(tdm_test_client *data)
600 {
601         tdm_client_voutput *voutput = NULL;
602         tdm_client_output *output = NULL;
603         tdm_client_output_mode modes[2];
604         tdm_error ret = TDM_ERROR_NONE;
605
606         printf("virtual output test - client\n");
607
608         voutput = tdm_client_create_voutput(data->client, "virtual-test", &ret);
609         TDM_EXIT_IF_FAIL(ret == TDM_ERROR_NONE);
610
611         ret = tdm_client_voutput_add_commit_handler(voutput, _voutput_commit, data);
612         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
613
614         output = tdm_client_voutput_get_client_output(voutput, &ret);
615         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
616
617         ret = tdm_client_voutput_set_physical_size(voutput, 300, 200);
618         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
619
620         _voutput_make_available_mode(modes, 2);
621         ret = tdm_client_voutput_set_available_modes(voutput, modes, 2);
622         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
623
624         ret = tdm_client_output_connect(output);
625         TDM_GOTO_IF_FAIL(ret == TDM_ERROR_NONE, done);
626
627         while (1) {
628                 tdm_client_handle_events_timeout(data->client, 1000);
629         }
630
631 done:
632         if (voutput)
633                 tdm_client_voutput_destroy(voutput);
634 }
635
636 static tdm_test_client ttc_data;
637
638 int
639 main(int argc, char *argv[])
640 {
641         tdm_test_client *data = &ttc_data;
642         tdm_error error;
643
644 #if 1 /* for testing */
645         const char *xdg = (const char*)getenv("XDG_RUNTIME_DIR");
646         if (!xdg) {
647                 char buf[32];
648                 snprintf(buf, sizeof(buf), "/run");
649                 int ret = setenv("XDG_RUNTIME_DIR", (const char*)buf, 1);
650                 if (ret != 0)
651                         exit(0);
652         }
653
654         /* for tbm_bufmgr_init */
655         const char *s  = (const char*)getenv("TBM_DISPLAY_SERVER");
656         if (!s) {
657                 char buf[32];
658                 snprintf(buf, sizeof(buf), "1");
659                 int ret = setenv("TBM_DISPLAY_SERVER", (const char*)buf, 1);
660                 if (ret != 0)
661                         exit(0);
662         }
663 #endif
664
665         parse_args(data, argc, argv);
666
667         printf("sync(%d) fps(%d) interval(%d) offset(%d) enable_fake(%d) pid(%d)\n",
668                    data->args.sync, data->args.fps, data->args.interval,
669                    data->args.offset, data->args.enable_fake, data->args.pid);
670
671         data->client = tdm_client_create(&error);
672         if (error != TDM_ERROR_NONE) {
673                 printf("tdm_client_create failed\n");
674                 goto done;
675         }
676
677         if (data->do_query)
678                 do_query(data);
679         if (data->do_vblank)
680                 do_vblank(data);
681         if (data->do_voutput)
682                 do_voutput(data);
683
684 done:
685         if (data->args.output_name)
686                 free(data->args.output_name);
687         if (data->args.vblank_name)
688                 free(data->args.vblank_name);
689         if (data->client)
690                 tdm_client_destroy(data->client);
691
692         return 0;
693 }