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