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