New, more flexible syntax for format
[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/mman.h>
23 #include <sys/ioctl.h>
24 #include <sys/time.h>
25 #include <sys/stat.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/types.h>
37 #include <linux/media.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 "v4l2subdev.h"
45 #include "tools.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_SUBDEV_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_SUBDEV_SEL_TGT_CROP_ACTUAL,
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         printf("]\n");
81 }
82
83 static const char *media_entity_type_to_string(unsigned type)
84 {
85         static const struct {
86                 __u32 type;
87                 const char *name;
88         } types[] = {
89                 { MEDIA_ENT_T_DEVNODE, "Node" },
90                 { MEDIA_ENT_T_V4L2_SUBDEV, "V4L2 subdev" },
91         };
92
93         unsigned int i;
94
95         type &= MEDIA_ENT_TYPE_MASK;
96
97         for (i = 0; i < ARRAY_SIZE(types); i++) {
98                 if (types[i].type == type)
99                         return types[i].name;
100         }
101
102         return "Unknown";
103 }
104
105 static const char *media_entity_subtype_to_string(unsigned type)
106 {
107         static const char *node_types[] = {
108                 "Unknown",
109                 "V4L",
110                 "FB",
111                 "ALSA",
112                 "DVB",
113         };
114         static const char *subdev_types[] = {
115                 "Unknown",
116                 "Sensor",
117                 "Flash",
118                 "Lens",
119         };
120
121         unsigned int subtype = type & MEDIA_ENT_SUBTYPE_MASK;
122
123         switch (type & MEDIA_ENT_TYPE_MASK) {
124         case MEDIA_ENT_T_DEVNODE:
125                 if (subtype >= ARRAY_SIZE(node_types))
126                         subtype = 0;
127                 return node_types[subtype];
128
129         case MEDIA_ENT_T_V4L2_SUBDEV:
130                 if (subtype >= ARRAY_SIZE(subdev_types))
131                         subtype = 0;
132                 return subdev_types[subtype];
133         default:
134                 return node_types[0];
135         }
136 }
137
138 static const char *media_pad_type_to_string(unsigned flag)
139 {
140         static const struct {
141                 __u32 flag;
142                 const char *name;
143         } flags[] = {
144                 { MEDIA_PAD_FL_SINK, "Sink" },
145                 { MEDIA_PAD_FL_SOURCE, "Source" },
146         };
147
148         unsigned int i;
149
150         for (i = 0; i < ARRAY_SIZE(flags); i++) {
151                 if (flags[i].flag & flag)
152                         return flags[i].name;
153         }
154
155         return "Unknown";
156 }
157
158 static void media_print_topology_dot(struct media_device *media)
159 {
160         unsigned int i, j;
161
162         printf("digraph board {\n");
163         printf("\trankdir=TB\n");
164
165         for (i = 0; i < media->entities_count; ++i) {
166                 struct media_entity *entity = &media->entities[i];
167                 unsigned int npads;
168
169                 switch (media_entity_type(entity)) {
170                 case MEDIA_ENT_T_DEVNODE:
171                         printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, "
172                                "fillcolor=yellow]\n",
173                                entity->info.id, entity->info.name, entity->devname);
174                         break;
175
176                 case MEDIA_ENT_T_V4L2_SUBDEV:
177                         printf("\tn%08x [label=\"{{", entity->info.id);
178
179                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
180                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SINK))
181                                         continue;
182
183                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
184                                 npads++;
185                         }
186
187                         printf("} | %s", entity->info.name);
188                         if (entity->devname)
189                                 printf("\\n%s", entity->devname);
190                         printf(" | {");
191
192                         for (j = 0, npads = 0; j < entity->info.pads; ++j) {
193                                 if (!(entity->pads[j].flags & MEDIA_PAD_FL_SOURCE))
194                                         continue;
195
196                                 printf("%s<port%u> %u", npads ? " | " : "", j, j);
197                                 npads++;
198                         }
199
200                         printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n");
201                         break;
202
203                 default:
204                         continue;
205                 }
206
207                 for (j = 0; j < entity->num_links; j++) {
208                         struct media_link *link = &entity->links[j];
209
210                         if (link->source->entity != entity)
211                                 continue;
212
213                         printf("\tn%08x", link->source->entity->info.id);
214                         if (media_entity_type(link->source->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
215                                 printf(":port%u", link->source->index);
216                         printf(" -> ");
217                         printf("n%08x", link->sink->entity->info.id);
218                         if (media_entity_type(link->sink->entity) == MEDIA_ENT_T_V4L2_SUBDEV)
219                                 printf(":port%u", link->sink->index);
220
221                         if (link->flags & MEDIA_LNK_FL_IMMUTABLE)
222                                 printf(" [style=bold]");
223                         else if (!(link->flags & MEDIA_LNK_FL_ENABLED))
224                                 printf(" [style=dashed]");
225                         printf("\n");
226                 }
227         }
228
229         printf("}\n");
230 }
231
232 static void media_print_topology_text(struct media_device *media)
233 {
234         static const struct {
235                 __u32 flag;
236                 char *name;
237         } link_flags[] = {
238                 { MEDIA_LNK_FL_ENABLED, "ENABLED" },
239                 { MEDIA_LNK_FL_IMMUTABLE, "IMMUTABLE" },
240                 { MEDIA_LNK_FL_DYNAMIC, "DYNAMIC" },
241         };
242
243         unsigned int i, j, k;
244         unsigned int padding;
245
246         printf("Device topology\n");
247
248         for (i = 0; i < media->entities_count; ++i) {
249                 struct media_entity *entity = &media->entities[i];
250
251                 padding = printf("- entity %u: ", entity->info.id);
252                 printf("%s (%u pad%s, %u link%s)\n", entity->info.name,
253                         entity->info.pads, entity->info.pads > 1 ? "s" : "",
254                         entity->num_links, entity->num_links > 1 ? "s" : "");
255                 printf("%*ctype %s subtype %s\n", padding, ' ',
256                         media_entity_type_to_string(entity->info.type),
257                         media_entity_subtype_to_string(entity->info.type));
258                 if (entity->devname[0])
259                         printf("%*cdevice node name %s\n", padding, ' ', entity->devname);
260
261                 for (j = 0; j < entity->info.pads; j++) {
262                         struct media_pad *pad = &entity->pads[j];
263
264                         printf("\tpad%u: %s\n", j, media_pad_type_to_string(pad->flags));
265
266                         if (media_entity_type(entity) == MEDIA_ENT_T_V4L2_SUBDEV)
267                                 v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE);
268
269                         for (k = 0; k < entity->num_links; k++) {
270                                 struct media_link *link = &entity->links[k];
271                                 struct media_pad *source = link->source;
272                                 struct media_pad *sink = link->sink;
273                                 bool first = true;
274                                 unsigned int i;
275
276                                 if (source->entity == entity && source->index == j)
277                                         printf("\t\t-> \"%s\":%u [",
278                                                 sink->entity->info.name, sink->index);
279                                 else if (sink->entity == entity && sink->index == j)
280                                         printf("\t\t<- \"%s\":%u [",
281                                                 source->entity->info.name, source->index);
282                                 else
283                                         continue;
284
285                                 for (i = 0; i < ARRAY_SIZE(link_flags); i++) {
286                                         if (!(link->flags & link_flags[i].flag))
287                                                 continue;
288                                         if (!first)
289                                                 printf(",");
290                                         printf("%s", link_flags[i].name);
291                                         first = false;
292                                 }
293
294                                 printf("]\n");
295                         }
296                 }
297                 printf("\n");
298         }
299 }
300
301 void media_print_topology(struct media_device *media, int dot)
302 {
303         if (dot)
304                 media_print_topology_dot(media);
305         else
306                 media_print_topology_text(media);
307 }
308
309 int main(int argc, char **argv)
310 {
311         struct media_device *media;
312         int ret = -1;
313
314         if (parse_cmdline(argc, argv))
315                 return EXIT_FAILURE;
316
317         /* Open the media device and enumerate entities, pads and links. */
318         if (media_opts.verbose)
319                 media = media_open_debug(
320                         media_opts.devname,
321                         (void (*)(void *, ...))fprintf, stdout);
322         else
323                 media = media_open(media_opts.devname);
324         if (media == NULL) {
325                 printf("Failed to open %s\n", media_opts.devname);
326                 goto out;
327         }
328
329         if (media_opts.print) {
330                 printf("Media controller API version %u.%u.%u\n\n",
331                        (media->info.media_version << 16) & 0xff,
332                        (media->info.media_version << 8) & 0xff,
333                        (media->info.media_version << 0) & 0xff);
334                 printf("Media device information\n"
335                        "------------------------\n"
336                        "driver          %s\n"
337                        "model           %s\n"
338                        "serial          %s\n"
339                        "bus info        %s\n"
340                        "hw revision     0x%x\n"
341                        "driver version  %u.%u.%u\n\n",
342                        media->info.driver, media->info.model,
343                        media->info.serial, media->info.bus_info,
344                        media->info.hw_revision,
345                        (media->info.driver_version << 16) & 0xff,
346                        (media->info.driver_version << 8) & 0xff,
347                        (media->info.driver_version << 0) & 0xff);
348         }
349
350         if (media_opts.entity) {
351                 struct media_entity *entity;
352
353                 entity = media_get_entity_by_name(media, media_opts.entity,
354                                                   strlen(media_opts.entity));
355                 if (entity == NULL) {
356                         printf("Entity '%s' not found\n", media_opts.entity);
357                         goto out;
358                 }
359
360                 printf("%s\n", entity->devname);
361         }
362
363         if (media_opts.pad) {
364                 struct media_pad *pad;
365
366                 pad = media_parse_pad(media, media_opts.pad, NULL);
367                 if (pad == NULL) {
368                         printf("Pad '%s' not found\n", media_opts.pad);
369                         goto out;
370                 }
371
372                 v4l2_subdev_print_format(pad->entity, pad->index,
373                                          V4L2_SUBDEV_FORMAT_ACTIVE);
374         }
375
376         if (media_opts.print || media_opts.print_dot) {
377                 media_print_topology(media, media_opts.print_dot);
378                 printf("\n");
379         }
380
381         if (media_opts.reset) {
382                 if (media_opts.verbose)
383                         printf("Resetting all links to inactive\n");
384                 ret = media_reset_links(media);
385                 if (ret) {
386                         printf("Unable to reset links: %s (%d)\n",
387                                strerror(-ret), -ret);
388                         goto out;
389                 }
390         }
391
392         if (media_opts.links) {
393                 ret = media_parse_setup_links(media, media_opts.links);
394                 if (ret) {
395                         printf("Unable to parse link: %s (%d)\n",
396                                strerror(-ret), -ret);
397                         goto out;
398                 }
399         }
400
401         if (media_opts.formats) {
402                 ret = v4l2_subdev_parse_setup_formats(media,
403                                                       media_opts.formats);
404                 if (ret) {
405                         printf("Unable to parse format: %s (%d)\n",
406                                strerror(-ret), -ret);
407                         goto out;
408                 }
409         }
410
411         if (media_opts.interactive) {
412                 while (1) {
413                         char buffer[32];
414                         char *end;
415
416                         printf("Enter a link to modify or enter to stop\n");
417                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
418                                 break;
419
420                         if (buffer[0] == '\n')
421                                 break;
422
423                         ret = media_parse_setup_link(media, buffer, &end);
424                         if (ret)
425                                 printf("Unable to parse link: %s (%d)\n",
426                                        strerror(-ret), -ret);
427                 }
428         }
429
430         ret = 0;
431
432 out:
433         if (media)
434                 media_close(media);
435
436         return ret ? EXIT_FAILURE : EXIT_SUCCESS;
437 }
438