Print video device node names in dot diagrams
[media-ctl.git] / main.c
1 /*
2  * Media controller test application
3  *
4  * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  */
19
20 #include <sys/mman.h>
21 #include <sys/ioctl.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24
25 #include <ctype.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <linux/types.h>
34 #include <linux/media.h>
35 #include <linux/v4l2-mediabus.h>
36 #include <linux/v4l2-subdev.h>
37 #include <linux/videodev2.h>
38
39 #include "media.h"
40 #include "options.h"
41 #include "subdev.h"
42
43 /* -----------------------------------------------------------------------------
44  * Links setup
45  */
46
47 static struct media_entity_pad *parse_pad(struct media_device *media, const char *p, char **endp)
48 {
49         unsigned int entity_id, pad;
50         struct media_entity *entity;
51         char *end;
52
53         for (; isspace(*p); ++p);
54
55         if (*p == '"') {
56                 for (end = (char *)p + 1; *end && *end != '"'; ++end);
57                 if (*end != '"')
58                         return NULL;
59
60                 entity = media_get_entity_by_name(media, p + 1, end - p - 1);
61                 if (entity == NULL)
62                         return NULL;
63
64                 ++end;
65         } else {
66                 entity_id = strtoul(p, &end, 10);
67                 entity = media_get_entity_by_id(media, entity_id);
68                 if (entity == NULL)
69                         return NULL;
70         }
71         for (; isspace(*end); ++end);
72
73         if (*end != ':')
74                 return NULL;
75         for (p = end + 1; isspace(*p); ++p);
76
77         pad = strtoul(p, &end, 10);
78         for (p = end; isspace(*p); ++p);
79
80         if (pad >= entity->info.pads)
81                 return NULL;
82
83         for (p = end; isspace(*p); ++p);
84         *endp = (char *)p;
85
86         return &entity->pads[pad];
87 }
88
89 static struct media_entity_link *parse_link(struct media_device *media, const char *p, char **endp)
90 {
91         struct media_entity_link *link;
92         struct media_entity_pad *source;
93         struct media_entity_pad *sink;
94         unsigned int i;
95         char *end;
96
97         source = parse_pad(media, p, &end);
98         if (source == NULL)
99                 return NULL;
100
101         if (end[0] != '-' || end[1] != '>')
102                 return NULL;
103         p = end + 2;
104
105         sink = parse_pad(media, p, &end);
106         if (sink == NULL)
107                 return NULL;
108
109         *endp = end;
110
111         for (i = 0; i < source->entity->info.links; i++) {
112                 link = &source->entity->links[i];
113
114                 if (link->source == source && link->sink == sink)
115                         return link;
116         }
117
118         return NULL;
119 }
120
121 static int setup_link(struct media_device *media, const char *p, char **endp)
122 {
123         struct media_entity_link *link;
124         __u32 flags;
125         char *end;
126
127         link = parse_link(media, p, &end);
128         if (link == NULL) {
129                 printf("Unable to parse link\n");
130                 return -EINVAL;
131         }
132
133         p = end;
134         if (*p++ != '[') {
135                 printf("Unable to parse link flags\n");
136                 return -EINVAL;
137         }
138
139         flags = strtoul(p, &end, 10);
140         for (p = end; isspace(*p); p++);
141         if (*p++ != ']') {
142                 printf("Unable to parse link flags\n");
143                 return -EINVAL;
144         }
145
146         for (; isspace(*p); p++);
147         *endp = (char *)p;
148
149         printf("Setting up link %u:%u -> %u:%u [%u]\n",
150                 link->source->entity->info.id, link->source->index,
151                 link->sink->entity->info.id, link->sink->index,
152                 flags);
153
154         return media_setup_link(media, link->source, link->sink, flags);
155 }
156
157 static int setup_links(struct media_device *media, const char *p)
158 {
159         char *end;
160         int ret;
161
162         do {
163                 ret = setup_link(media, p, &end);
164                 if (ret < 0)
165                         return ret;
166
167                 p = end + 1;
168         } while (*end == ',');
169
170         return *end ? -EINVAL : 0;
171 }
172
173 /* -----------------------------------------------------------------------------
174  * Formats setup
175  */
176
177 static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp)
178 {
179         enum v4l2_mbus_pixelcode code;
180         unsigned int width, height;
181         char *end;
182
183         for (; isspace(*p); ++p);
184         for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end);
185
186         code = string_to_pixelcode(p, end - p);
187         if (code == (enum v4l2_mbus_pixelcode)-1)
188                 return -EINVAL;
189
190         for (p = end; isspace(*p); ++p);
191         width = strtoul(p, &end, 10);
192         if (*end != 'x')
193                 return -EINVAL;
194
195         p = end + 1;
196         height = strtoul(p, &end, 10);
197         *endp = end;
198
199         memset(format, 0, sizeof(*format));
200         format->width = width;
201         format->height = height;
202         format->code = code;
203
204         return 0;
205 }
206
207 static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp)
208 {
209         char *end;
210
211         for (; isspace(*p); ++p);
212
213         crop->left = strtoul(p, &end, 10);
214         if (*end != ',')
215                 return -EINVAL;
216
217         p = end + 1;
218         crop->top = strtoul(p, &end, 10);
219         if (*end != '/')
220                 return -EINVAL;
221
222         p = end + 1;
223         crop->width = strtoul(p, &end, 10);
224         if (*end != 'x')
225                 return -EINVAL;
226
227         p = end + 1;
228         crop->height = strtoul(p, &end, 10);
229         *endp = end;
230
231         return 0;
232 }
233
234 static int parse_frame_interval(struct v4l2_fract *interval, const char *p, char **endp)
235 {
236         char *end;
237
238         for (; isspace(*p); ++p);
239
240         interval->numerator = strtoul(p, &end, 10);
241
242         for (p = end; isspace(*p); ++p);
243         if (*p++ != '/')
244                 return -EINVAL;
245
246         for (; isspace(*p); ++p);
247         interval->denominator = strtoul(p, &end, 10);
248
249         *endp = end;
250         return 0;
251 }
252
253 static struct media_entity_pad *parse_pad_format(struct media_device *media,
254         struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop,
255         struct v4l2_fract *interval, const char *p, char **endp)
256 {
257         struct media_entity_pad *pad;
258         char *end;
259         int ret;
260
261         for (; isspace(*p); ++p);
262
263         pad = parse_pad(media, p, &end);
264         if (pad == NULL)
265                 return NULL;
266
267         for (p = end; isspace(*p); ++p);
268         if (*p++ != '[')
269                 return NULL;
270
271         ret = parse_format(format, p, &end);
272         if (ret < 0)
273                 return NULL;
274
275         for (p = end; isspace(*p); p++);
276         if (isdigit(*p)) {
277                 ret = parse_crop(crop, p, &end);
278                 if (ret < 0)
279                         return NULL;
280
281                 for (p = end; isspace(*p); p++);
282         }
283
284         if (*p == '@') {
285                 ret = parse_frame_interval(interval, ++p, &end);
286                 if (ret < 0)
287                         return NULL;
288
289                 for (p = end; isspace(*p); p++);
290         }
291
292         if (*p != ']')
293                 return NULL;
294
295         *endp = (char *)p + 1;
296         return pad;
297 }
298
299 static int set_format(struct media_entity_pad *pad, struct v4l2_mbus_framefmt *format)
300 {
301         int ret;
302
303         printf("Setting up format %s %ux%u on pad %s/%u\n",
304                 pixelcode_to_string(format->code), format->width, format->height,
305                 pad->entity->info.name, pad->index);
306
307         ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
308                                      V4L2_SUBDEV_FORMAT_ACTIVE);
309         if (ret < 0) {
310                 printf("Unable to set format: %s (%d)\n", strerror(-ret), ret);
311                 return ret;
312         }
313
314         printf("Format set: %s %ux%u\n",
315                 pixelcode_to_string(format->code), format->width, format->height);
316
317         return 0;
318 }
319
320 static int set_crop(struct media_entity_pad *pad, struct v4l2_rect *crop)
321 {
322         int ret;
323
324         printf("Setting up crop rectangle %u,%u/%ux%u on pad %s/%u\n",
325                 crop->left, crop->top, crop->width, crop->height,
326                 pad->entity->info.name, pad->index);
327
328         ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index,
329                                    V4L2_SUBDEV_FORMAT_ACTIVE);
330         if (ret < 0) {
331                 printf("Unable to set crop rectangle: %s (%d)", strerror(-ret), ret);
332                 return ret;
333         }
334
335         printf("Crop rectangle set: %u,%u/%ux%u\n",
336                 crop->left, crop->top, crop->width, crop->height);
337
338         return 0;
339 }
340
341 static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval)
342 {
343         int ret;
344
345         printf("Setting up frame interval %u/%u on entity %s\n",
346                 interval->numerator, interval->denominator, entity->info.name);
347
348         ret = v4l2_subdev_set_frame_interval(entity, interval);
349         if (ret < 0) {
350                 printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret);
351                 return ret;
352         }
353
354         printf("Frame interval set: %u/%u\n",
355                 interval->numerator, interval->denominator);
356
357         return 0;
358 }
359
360
361 static int setup_format(struct media_device *media, const char *p, char **endp)
362 {
363         struct v4l2_mbus_framefmt format;
364         struct media_entity_pad *pad;
365         struct v4l2_rect crop = { -1, -1, -1, -1 };
366         struct v4l2_fract interval = { 0, 0 };
367         unsigned int i;
368         char *end;
369         int ret;
370
371         pad = parse_pad_format(media, &format, &crop, &interval, p, &end);
372         if (pad == NULL) {
373                 printf("Unable to parse format\n");
374                 return -EINVAL;
375         }
376
377         ret = set_format(pad, &format);
378         if (ret < 0) {
379                 printf("Unable to set format\n");
380                 return ret;
381         }
382
383         if (crop.left != -1 && crop.top != -1) {
384                 ret = set_crop(pad, &crop);
385                 if (ret < 0) {
386                         printf("Unable to set crop rectangle\n");
387                         return ret;
388                 }
389         }
390
391         if (interval.numerator != 0) {
392                 ret = set_frame_interval(pad->entity, &interval);
393                 if (ret < 0) {
394                         printf("Unable to set frame interval\n");
395                         return ret;
396                 }
397         }
398
399         /* If the pad is an output pad, automatically set the same format on
400          * the remote subdev input pads, if any.
401          */
402         if (pad->type == MEDIA_PAD_TYPE_OUTPUT) {
403                 for (i = 0; i < pad->entity->info.links; ++i) {
404                         struct media_entity_link *link = &pad->entity->links[i];
405                         struct v4l2_mbus_framefmt remote_format;
406
407                         if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
408                                 continue;
409
410                         if (link->source == pad &&
411                             link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) {
412                                 remote_format = format;
413                                 set_format(link->sink, &remote_format);
414                         }
415                 }
416         }
417
418         *endp = end;
419         return 0;
420 }
421
422 static int setup_formats(struct media_device *media, const char *p)
423 {
424         char *end;
425         int ret;
426
427         do {
428                 ret = setup_format(media, p, &end);
429                 if (ret < 0)
430                         return ret;
431
432                 p = end + 1;
433         } while (*end == ',');
434
435         return *end ? -EINVAL : 0;
436 }
437
438 int main(int argc, char **argv)
439 {
440         struct media_device *media;
441
442         if (parse_cmdline(argc, argv))
443                 return EXIT_FAILURE;
444
445         /* Open the media device and enumerate entities, pads and links. */
446         media = media_open(media_opts.devname, media_opts.verbose);
447         if (media == NULL)
448                 goto out;
449
450         if (media_opts.entity) {
451                 struct media_entity *entity;
452
453                 entity = media_get_entity_by_name(media, media_opts.entity,
454                                                   strlen(media_opts.entity));
455                 if (entity != NULL)
456                         printf("%s\n", entity->devname);
457                 else
458                         printf("Entity '%s' not found\n", media_opts.entity);
459         }
460
461         if (media_opts.print || media_opts.print_dot) {
462                 media_print_topology(media, media_opts.print_dot);
463                 printf("\n");
464         }
465
466         if (media_opts.reset) {
467                 printf("Resetting all links to inactive\n");
468                 media_reset_links(media);
469         }
470
471         if (media_opts.links)
472                 setup_links(media, media_opts.links);
473
474         if (media_opts.formats)
475                 setup_formats(media, media_opts.formats);
476
477         if (media_opts.interactive) {
478                 while (1) {
479                         char buffer[32];
480                         char *end;
481
482                         printf("Enter a link to modify or enter to stop\n");
483                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
484                                 break;
485
486                         if (buffer[0] == '\n')
487                                 break;
488
489                         setup_link(media, buffer, &end);
490                 }
491         }
492
493 out:
494         if (media)
495                 media_close(media);
496
497         exit(EXIT_SUCCESS);
498 }
499