b0e227750c435803286091c8ab5a04fa257ca89d
[media-ctl.git] / src / main.c
1 /*
2  * Media controller test application
3  *
4  * Copyright (C) 2010-2011 Ideas on board SPRL
5  *
6  * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <sys/ioctl.h>
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include <linux/media.h>
37 #include <linux/types.h>
38 #include <linux/v4l2-mediabus.h>
39 #include <linux/v4l2-subdev.h>
40 #include <linux/videodev2.h>
41
42 #include "mediactl.h"
43 #include "options.h"
44 #include "tools.h"
45 #include "v4l2subdev.h"
46
47 /* -----------------------------------------------------------------------------
48  * Printing
49  */
50
51 static void v4l2_subdev_print_format(struct media_entity *entity,
52         unsigned int pad, enum v4l2_subdev_format_whence which)
53 {
54         struct v4l2_mbus_framefmt format;
55         struct v4l2_rect rect;
56         int ret;
57
58         ret = v4l2_subdev_get_format(entity, &format, pad, which);
59         if (ret != 0)
60                 return;
61
62         printf("\t\t[fmt:%s/%ux%u",
63                v4l2_subdev_pixelcode_to_string(format.code),
64                format.width, format.height);
65
66         ret = v4l2_subdev_get_selection(entity, &rect, pad,
67                                         V4L2_SEL_TGT_CROP_BOUNDS,
68                                         which);
69         if (ret == 0)
70                 printf("\n\t\t crop.bounds:(%u,%u)/%ux%u", rect.left, rect.top,
71                        rect.width, rect.height);
72
73         ret = v4l2_subdev_get_selection(entity, &rect, pad,
74                                         V4L2_SEL_TGT_CROP,
75                                         which);
76         if (ret == 0)
77                 printf("\n\t\t crop:(%u,%u)/%ux%u", rect.left, rect.top,
78                        rect.width, rect.height);
79
80         ret = v4l2_subdev_get_selection(entity, &rect, pad,
81                                         V4L2_SEL_TGT_COMPOSE_BOUNDS,
82                                         which);
83         if (ret == 0)
84                 printf("\n\t\t compose.bounds:(%u,%u)/%ux%u",
85                        rect.left, rect.top, rect.width, rect.height);
86
87         ret = v4l2_subdev_get_selection(entity, &rect, pad,
88                                         V4L2_SEL_TGT_COMPOSE,
89                                         which);
90         if (ret == 0)
91                 printf("\n\t\t compose:(%u,%u)/%ux%u",
92                        rect.left, rect.top, rect.width, rect.height);
93
94         printf("]\n");
95 }
96
97 static const char *media_entity_type_to_string(unsigned type)
98 {
99         static const struct {
100                 __u32 type;
101                 const char *name;
102         } types[] = {
103                 { MEDIA_ENT_T_DEVNODE, "Node" },
104                 { MEDIA_ENT_T_V4L2_SUBDEV, "V4L2 subdev" },
105         };
106
107         unsigned int i;
108
109         type &= MEDIA_ENT_TYPE_MASK;
110
111         for (i = 0; i < ARRAY_SIZE(types); i++) {
112                 if (types[i].type == type)
113                         return types[i].name;
114         }
115
116         return "Unknown";
117 }
118
119 static const char *media_entity_subtype_to_string(unsigned type)
120 {
121         static const char *node_types[] = {
122                 "Unknown",
123                 "V4L",
124                 "FB",
125                 "ALSA",
126                 "DVB",
127         };
128         static const char *subdev_types[] = {
129                 "Unknown",
130                 "Sensor",
131                 "Flash",
132                 "Lens",
133         };
134
135         unsigned int subtype = type & MEDIA_ENT_SUBTYPE_MASK;
136
137         switch (type & MEDIA_ENT_TYPE_MASK) {
138         case MEDIA_ENT_T_DEVNODE:
139                 if (subtype >= ARRAY_SIZE(node_types))
140                         subtype = 0;
141                 return node_types[subtype];
142
143         case MEDIA_ENT_T_V4L2_SUBDEV:
144                 if (subtype >= ARRAY_SIZE(subdev_types))
145                         subtype = 0;
146                 return subdev_types[subtype];
147         default:
148                 return node_types[0];
149         }
150 }
151
152 static const char *media_pad_type_to_string(unsigned flag)
153 {
154         static const struct {
155                 __u32 flag;
156                 const char *name;
157         } flags[] = {
158                 { MEDIA_PAD_FL_SINK, "Sink" },
159                 { MEDIA_PAD_FL_SOURCE, "Source" },
160         };
161
162         unsigned int i;
163
164         for (i = 0; i < ARRAY_SIZE(flags); i++) {
165                 if (flags[i].flag & flag)
166                         return flags[i].name;
167         }
168
169         return "Unknown";
170 }
171
172 static void media_print_topology_dot(struct media_device *media)
173 {
174         struct media_entity *entities = media_get_entities(media);
175         unsigned int nents = media_get_entities_count(media);
176         unsigned int i, j;
177
178         printf("digraph board {\n");
179         printf("\trankdir=TB\n");
180
181         for (i = 0; i < nents; ++i) {
182                 struct media_entity *entity = &entities[i];
183                 unsigned int npads;
184
185                 switch (media_entity_type(entity)) {
186                 case MEDIA_ENT_T_DEVNODE:
187                         printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, "
188                                "fillcolor=yellow]\n",
189                                entity->info.id, entity->info.name, entity->devname);
190                         break;
191
192                 case MEDIA_ENT_T_V4L2_SUBDEV:
193                         printf("\tn%08x [label=\"{{", entity->info.id);
194
195                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
196                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SINK))
197                                         continue;
198
199                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
200                                 npads++;
201                         }
202
203                         printf("} | %s", entity->info.name);
204                         if (entity->devname)
205                                 printf("\\n%s", entity->devname);
206                         printf(" | {");
207
208                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
209                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SOURCE))
210                                         continue;
211
212                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
213                                 npads++;
214                         }
215
216                         printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
217                         break;
218
219                 default:
220                         continue;
221                 }
222
223                 for (j = 0; j < entity->num_links; j++) {
224                         struct media_link *link = &entity->links[j];
225
226                         if (link->source->entity != entity)
227                                 continue;
228
229                         printf("\tn%08x", link->source->entity->info.id);
230                         if (media_entity_type(link->source->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
231                                 printf(":port%u", link->source->index);
232                         printf(" -> ");
233                         printf("n%08x", link->sink->entity->info.id);
234                         if (media_entity_type(link->sink->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
235                                 printf(":port%u", link->sink->index);
236
237                         if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
238                                 printf(" [style=bold]");
239                         else if (!(link->flags & MEDIA_LNK_FL_ENABLED))
240                                 printf(" [style=dashed]");
241                         printf("\n");
242                 }
243         }
244
245         printf("}\n");
246 }
247
248 static void media_print_topology_text(struct media_device *media)
249 {
250         static const struct {
251                 __u32 flag;
252                 char *name;
253         } link_flags[] = {
254                 { MEDIA_LNK_FL_ENABLED, "ENABLED" },
255                 { MEDIA_LNK_FL_IMMUTABLE, "IMMUTABLE" },
256                 { MEDIA_LNK_FL_DYNAMIC, "DYNAMIC" },
257         };
258
259         struct media_entity *entities = media_get_entities(media);
260         unsigned int nents = media_get_entities_count(media);
261         unsigned int i, j, k;
262         unsigned int padding;
263
264         printf("Device topology\n");
265
266         for (i = 0; i < nents; ++i) {
267                 struct media_entity *entity = &entities[i];
268
269                 padding = printf("- entity %u: ", entity->info.id);
270                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
271                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
272                         entity->num_links, entity->num_links > 1 ? "s" : "");
273                 printf("%*ctype %s subtype %s flags %x\n", padding, ' ',
274                         media_entity_type_to_string(entity->info.type),
275                         media_entity_subtype_to_string(entity->info.type),
276                         entity->info.flags);
277                 if (entity->devname[0])
278                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
279
280                 for (j = 0; j < entity->info.pads; j++) {
281                         struct media_pad *pad = &entity->pads[j];
282
283                         printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags));
284
285                         if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
286                                 v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE);
287
288                         for (k = 0; k < entity->num_links; k++) {
289                                 struct media_link *link = &entity->links[k];
290                                 struct media_pad *source = link->source;
291                                 struct media_pad *sink = link->sink;
292                                 bool first = true;
293                                 unsigned int i;
294
295                                 if (source->entity == entity && source->index == j)
296                                         printf("\t\t-> \"%s\":%u [",
297                                                 sink->entity->info.name, sink->index);
298                                 else if (sink->entity == entity && sink->index == j)
299                                         printf("\t\t<- \"%s\":%u [",
300                                                 source->entity->info.name, source->index);
301                                 else
302                                         continue;
303
304                                 for (i = 0; i < ARRAY_SIZE(link_flags); i++) {
305                                         if (!(link->flags & link_flags[i].flag))
306                                                 continue;
307                                         if (!first)
308                                                 printf(",");
309                                         printf("%s", link_flags[i].name);
310                                         first = false;
311                                 }
312
313                                 printf("]\n");
314                         }
315                 }
316                 printf("\n");
317         }
318 }
319
320 void media_print_topology(struct media_device *media, int dot)
321 {
322         if (dot)
323                 media_print_topology_dot(media);
324         else
325                 media_print_topology_text(media);
326 }
327
328 int main(int argc, char **argv)
329 {
330         struct media_device *media;
331         int ret = -1;
332
333         if (parse_cmdline(argc, argv))
334                 return EXIT_FAILURE;
335
336         media = media_device_new(media_opts.devname);
337         if (media == NULL) {
338                 printf("Failed to create media device\n");
339                 goto out;
340         }
341
342         if (media_opts.verbose)
343                 media_debug_set_handler(media,
344                         (void (*)(void *, ...))fprintf, stdout);
345
346         /* Enumerate entities, pads and links. */
347         ret = media_device_enumerate(media);
348         if (ret < 0) {
349                 printf("Failed to enumerate %s (%d)\n", media_opts.devname, ret);
350                 goto out;
351         }
352
353         if (media_opts.print) {
354                 const struct media_device_info *info = media_get_info(media);
355
356                 printf("Media controller API version %u.%u.%u\n\n",
357                        (info->media_version << 16) & 0xff,
358                        (info->media_version << 8) & 0xff,
359                        (info->media_version << 0) & 0xff);
360                 printf("Media device information\n"
361                        "------------------------\n"
362                        "driver          %s\n"
363                        "model           %s\n"
364                        "serial          %s\n"
365                        "bus info        %s\n"
366                        "hw revision     0x%x\n"
367                        "driver version  %u.%u.%u\n\n",
368                        info->driver, info->model,
369                        info->serial, info->bus_info,
370                        info->hw_revision,
371                        (info->driver_version << 16) & 0xff,
372                        (info->driver_version << 8) & 0xff,
373                        (info->driver_version << 0) & 0xff);
374         }
375
376         if (media_opts.entity) {
377                 struct media_entity *entity;
378
379                 entity = media_get_entity_by_name(media, media_opts.entity,
380                                                   strlen(media_opts.entity));
381                 if (entity == NULL) {
382                         printf("Entity '%s' not found\n", media_opts.entity);
383                         goto out;
384                 }
385
386                 printf("%s\n", entity->devname);
387         }
388
389         if (media_opts.pad) {
390                 struct media_pad *pad;
391
392                 pad = media_parse_pad(media, media_opts.pad, NULL);
393                 if (pad == NULL) {
394                         printf("Pad '%s' not found\n", media_opts.pad);
395                         goto out;
396                 }
397
398                 v4l2_subdev_print_format(pad->entity, pad->index,
399                                          V4L2_SUBDEV_FORMAT_ACTIVE);
400         }
401
402         if (media_opts.print || media_opts.print_dot) {
403                 media_print_topology(media, media_opts.print_dot);
404                 printf("\n");
405         }
406
407         if (media_opts.reset) {
408                 if (media_opts.verbose)
409                         printf("Resetting all links to inactive\n");
410                 ret = media_reset_links(media);
411                 if (ret) {
412                         printf("Unable to reset links: %s (%d)\n",
413                                strerror(-ret), -ret);
414                         goto out;
415                 }
416         }
417
418         if (media_opts.links) {
419                 ret = media_parse_setup_links(media, media_opts.links);
420                 if (ret) {
421                         printf("Unable to parse link: %s (%d)\n",
422                                strerror(-ret), -ret);
423                         goto out;
424                 }
425         }
426
427         if (media_opts.formats) {
428                 ret = v4l2_subdev_parse_setup_formats(media,
429                                                       media_opts.formats);
430                 if (ret) {
431                         printf("Unable to setup formats: %s (%d)\n",
432                                strerror(-ret), -ret);
433                         goto out;
434                 }
435         }
436
437         if (media_opts.interactive) {
438                 while (1) {
439                         char buffer[32];
440                         char *end;
441
442                         printf("Enter a link to modify or enter to stop\n");
443                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
444                                 break;
445
446                         if (buffer[0] == '\n')
447                                 break;
448
449                         ret = media_parse_setup_link(media, buffer, &end);
450                         if (ret)
451                                 printf("Unable to parse link: %s (%d)\n",
452                                        strerror(-ret), -ret);
453                 }
454         }
455
456         ret = 0;
457
458 out:
459         if (media)
460                 media_device_unref(media);
461
462         return ret ? EXIT_FAILURE : EXIT_SUCCESS;
463 }
464