strerror takes a non-negative integer
[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                 for (++end; isspace(*end); ++end);
65         } else {
66                 entity_id = strtoul(p, &end, 10) - 1;
67                 if (entity_id >= media->entities_count)
68                         return NULL;
69
70                 entity = &media->entities[entity_id];
71         }
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 struct media_entity_pad *parse_pad_format(struct media_device *media,
208         struct v4l2_mbus_framefmt *format, const char *p, char **endp)
209 {
210         struct media_entity_pad *pad;
211         char *end;
212         int ret;
213
214         for (; isspace(*p); ++p);
215
216         pad = parse_pad(media, p, &end);
217         if (pad == NULL)
218                 return NULL;
219
220         for (p = end; isspace(*p); ++p);
221         if (*p++ != '[')
222                 return NULL;
223
224         ret = parse_format(format, p, &end);
225         if (ret < 0)
226                 return NULL;
227
228         for (p = end; isspace(*p); p++);
229         if (*p++ != ']')
230                 return NULL;
231
232         *endp = (char *)p;
233         return pad;
234 }
235
236 static int set_format(struct media_entity_pad *pad, struct v4l2_mbus_framefmt *format)
237 {
238         int ret;
239
240         printf("Setting up format %s %ux%u on pad %s/%u\n",
241                 pixelcode_to_string(format->code), format->width, format->height,
242                 pad->entity->info.name, pad->index);
243
244         ret = v4l2_subdev_set_format(pad->entity, format, pad->index,
245                                      V4L2_SUBDEV_FORMAT_ACTIVE);
246         if (ret < 0) {
247                 printf("Unable to set format: %s (%d)\n", strerror(-ret), ret);
248                 return ret;
249         }
250
251         printf("Format set: %s %ux%u\n",
252                 pixelcode_to_string(format->code), format->width, format->height);
253
254         return 0;
255 }
256
257 static int setup_format(struct media_device *media, const char *p, char **endp)
258 {
259         struct v4l2_mbus_framefmt format;
260         struct media_entity_pad *pad;
261         unsigned int i;
262         char *end;
263         int ret;
264
265         pad = parse_pad_format(media, &format, p, &end);
266         if (pad == NULL) {
267                 printf("Unable to parse format\n");
268                 return -EINVAL;
269         }
270
271         ret = set_format(pad, &format);
272         if (ret < 0)
273                 return ret;
274
275         /* If the pad is an output pad, automatically set the same format on
276          * the remote subdev input pads, if any.
277          */
278         if (pad->type == MEDIA_PAD_TYPE_OUTPUT) {
279                 for (i = 0; i < pad->entity->info.links; ++i) {
280                         struct media_entity_link *link = &pad->entity->links[i];
281                         struct v4l2_mbus_framefmt remote_format;
282
283                         if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE))
284                                 continue;
285
286                         if (link->source == pad &&
287                             link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) {
288                                 remote_format = format;
289                                 set_format(link->sink, &remote_format);
290                         }
291                 }
292         }
293
294         *endp = end;
295         return 0;
296 }
297
298 static int setup_formats(struct media_device *media, const char *p)
299 {
300         char *end;
301         int ret;
302
303         do {
304                 ret = setup_format(media, p, &end);
305                 if (ret < 0) {
306                         printf("Unable to parse format\n");
307                         return ret;
308                 }
309
310                 p = end + 1;
311         } while (*end == ',');
312
313         return *end ? -EINVAL : 0;
314 }
315
316 int main(int argc, char **argv)
317 {
318         struct media_device *media;
319
320         if (parse_cmdline(argc, argv))
321                 return EXIT_FAILURE;
322
323         /* Open the media device and enumerate entities, pads and links. */
324         media = media_open(media_opts.devname, media_opts.verbose);
325         if (media == NULL)
326                 goto out;
327
328         if (media_opts.entity) {
329                 struct media_entity *entity;
330
331                 entity = media_get_entity_by_name(media, media_opts.entity,
332                                                   strlen(media_opts.entity));
333                 if (entity != NULL)
334                         printf("%s\n", entity->devname);
335                 else
336                         printf("Entity '%s' not found\n", media_opts.entity);
337         }
338
339         if (media_opts.print || media_opts.print_dot) {
340                 media_print_topology(media, media_opts.print_dot);
341                 printf("\n");
342         }
343
344         if (media_opts.reset) {
345                 printf("Resetting all links to inactive\n");
346                 media_reset_links(media);
347         }
348
349         if (media_opts.links)
350                 setup_links(media, media_opts.links);
351
352         if (media_opts.formats)
353                 setup_formats(media, media_opts.formats);
354
355         if (media_opts.interactive) {
356                 while (1) {
357                         char buffer[32];
358                         char *end;
359
360                         printf("Enter a link to modify or enter to stop\n");
361                         if (fgets(buffer, sizeof buffer, stdin) == NULL)
362                                 break;
363
364                         if (buffer[0] == '\n')
365                                 break;
366
367                         setup_link(media, buffer, &end);
368                 }
369         }
370
371 out:
372         if (media)
373                 media_close(media);
374
375         exit(EXIT_SUCCESS);
376 }
377