1 /*
2 * Media controller interface library
3 *
4 * Copyright (C) 2010-2014 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 "config.h"
23
24 #include <sys/ioctl.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <sys/types.h>
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37
38 #include <linux/media.h>
39 #include <linux/videodev2.h>
40
41 #include "mediactl.h"
42 #include "mediactl-priv.h"
43 #include "tools.h"
44
45 /* -----------------------------------------------------------------------------
46 * Graph access
47 */
48
media_entity_remote_source(struct media_pad * pad)49 struct media_pad *media_entity_remote_source(struct media_pad *pad)
50 {
51 unsigned int i;
52
53 if (!(pad->flags & MEDIA_PAD_FL_SINK))
54 return NULL;
55
56 for (i = 0; i < pad->entity->num_links; ++i) {
57 struct media_link *link = &pad->entity->links[i];
58
59 if (!(link->flags & MEDIA_LNK_FL_ENABLED))
60 continue;
61
62 if (link->sink == pad)
63 return link->source;
64 }
65
66 return NULL;
67 }
68
media_get_entity_by_name(struct media_device * media,const char * name)69 struct media_entity *media_get_entity_by_name(struct media_device *media,
70 const char *name)
71 {
72 unsigned int i;
73
74 for (i = 0; i < media->entities_count; ++i) {
75 struct media_entity *entity = &media->entities[i];
76
77 if (strcmp(entity->info.name, name) == 0)
78 return entity;
79 }
80
81 return NULL;
82 }
83
media_get_entity_by_id(struct media_device * media,uint32_t id)84 struct media_entity *media_get_entity_by_id(struct media_device *media,
85 uint32_t id)
86 {
87 bool next = id & MEDIA_ENT_ID_FLAG_NEXT;
88 unsigned int i;
89
90 id &= ~MEDIA_ENT_ID_FLAG_NEXT;
91
92 for (i = 0; i < media->entities_count; ++i) {
93 struct media_entity *entity = &media->entities[i];
94
95 if ((entity->info.id == id && !next) ||
96 (entity->info.id > id && next))
97 return entity;
98 }
99
100 return NULL;
101 }
102
media_get_entities_count(struct media_device * media)103 unsigned int media_get_entities_count(struct media_device *media)
104 {
105 return media->entities_count;
106 }
107
media_get_entity(struct media_device * media,unsigned int index)108 struct media_entity *media_get_entity(struct media_device *media, unsigned int index)
109 {
110 if (index >= media->entities_count)
111 return NULL;
112
113 return &media->entities[index];
114 }
115
media_entity_get_pad(struct media_entity * entity,unsigned int index)116 const struct media_pad *media_entity_get_pad(struct media_entity *entity, unsigned int index)
117 {
118 if (index >= entity->info.pads)
119 return NULL;
120
121 return &entity->pads[index];
122 }
123
media_entity_get_links_count(struct media_entity * entity)124 unsigned int media_entity_get_links_count(struct media_entity *entity)
125 {
126 return entity->num_links;
127 }
128
media_entity_get_link(struct media_entity * entity,unsigned int index)129 const struct media_link *media_entity_get_link(struct media_entity *entity, unsigned int index)
130 {
131 if (index >= entity->num_links)
132 return NULL;
133
134 return &entity->links[index];
135 }
136
media_entity_get_devname(struct media_entity * entity)137 const char *media_entity_get_devname(struct media_entity *entity)
138 {
139 return entity->devname[0] ? entity->devname : NULL;
140 }
141
media_get_default_entity(struct media_device * media,unsigned int type)142 struct media_entity *media_get_default_entity(struct media_device *media,
143 unsigned int type)
144 {
145 switch (type) {
146 case MEDIA_ENT_T_DEVNODE_V4L:
147 return media->def.v4l;
148 case MEDIA_ENT_T_DEVNODE_FB:
149 return media->def.fb;
150 case MEDIA_ENT_T_DEVNODE_ALSA:
151 return media->def.alsa;
152 case MEDIA_ENT_T_DEVNODE_DVB:
153 return media->def.dvb;
154 }
155
156 return NULL;
157 }
158
media_get_info(struct media_device * media)159 const struct media_device_info *media_get_info(struct media_device *media)
160 {
161 return &media->info;
162 }
163
media_get_devnode(struct media_device * media)164 const char *media_get_devnode(struct media_device *media)
165 {
166 return media->devnode;
167 }
168
media_entity_get_info(struct media_entity * entity)169 const struct media_entity_desc *media_entity_get_info(struct media_entity *entity)
170 {
171 return &entity->info;
172 }
173
174 /* -----------------------------------------------------------------------------
175 * Open/close
176 */
177
media_device_open(struct media_device * media)178 static int media_device_open(struct media_device *media)
179 {
180 int ret;
181
182 if (media->fd != -1)
183 return 0;
184
185 media_dbg(media, "Opening media device %s\n", media->devnode);
186
187 media->fd = open(media->devnode, O_RDWR);
188 if (media->fd < 0) {
189 ret = -errno;
190 media_dbg(media, "%s: Can't open media device %s\n",
191 __func__, media->devnode);
192 return ret;
193 }
194
195 return 0;
196 }
197
media_device_close(struct media_device * media)198 static void media_device_close(struct media_device *media)
199 {
200 if (media->fd != -1) {
201 close(media->fd);
202 media->fd = -1;
203 }
204 }
205
206 /* -----------------------------------------------------------------------------
207 * Link setup
208 */
209
media_setup_link(struct media_device * media,struct media_pad * source,struct media_pad * sink,uint32_t flags)210 int media_setup_link(struct media_device *media,
211 struct media_pad *source,
212 struct media_pad *sink,
213 uint32_t flags)
214 {
215 struct media_link_desc ulink = { { 0 } };
216 struct media_link *link;
217 unsigned int i;
218 int ret;
219
220 ret = media_device_open(media);
221 if (ret < 0)
222 goto done;
223
224 for (i = 0; i < source->entity->num_links; i++) {
225 link = &source->entity->links[i];
226
227 if (link->source->entity == source->entity &&
228 link->source->index == source->index &&
229 link->sink->entity == sink->entity &&
230 link->sink->index == sink->index)
231 break;
232 }
233
234 if (i == source->entity->num_links) {
235 media_dbg(media, "%s: Link not found\n", __func__);
236 ret = -ENOENT;
237 goto done;
238 }
239
240 /* source pad */
241 ulink.source.entity = source->entity->info.id;
242 ulink.source.index = source->index;
243 ulink.source.flags = MEDIA_PAD_FL_SOURCE;
244
245 /* sink pad */
246 ulink.sink.entity = sink->entity->info.id;
247 ulink.sink.index = sink->index;
248 ulink.sink.flags = MEDIA_PAD_FL_SINK;
249
250 ulink.flags = flags | (link->flags & MEDIA_LNK_FL_IMMUTABLE);
251
252 ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink);
253 if (ret == -1) {
254 ret = -errno;
255 media_dbg(media, "%s: Unable to setup link (%s)\n",
256 __func__, strerror(errno));
257 goto done;
258 }
259
260 link->flags = ulink.flags;
261 link->twin->flags = ulink.flags;
262
263 ret = 0;
264
265 done:
266 media_device_close(media);
267 return ret;
268 }
269
media_reset_links(struct media_device * media)270 int media_reset_links(struct media_device *media)
271 {
272 unsigned int i, j;
273 int ret;
274
275 for (i = 0; i < media->entities_count; ++i) {
276 struct media_entity *entity = &media->entities[i];
277
278 for (j = 0; j < entity->num_links; j++) {
279 struct media_link *link = &entity->links[j];
280
281 if (link->flags & MEDIA_LNK_FL_IMMUTABLE ||
282 link->source->entity != entity)
283 continue;
284
285 ret = media_setup_link(media, link->source, link->sink,
286 link->flags & ~MEDIA_LNK_FL_ENABLED);
287 if (ret < 0)
288 return ret;
289 }
290 }
291
292 return 0;
293 }
294
295 /* -----------------------------------------------------------------------------
296 * Entities, pads and links enumeration
297 */
298
media_entity_add_link(struct media_entity * entity)299 static struct media_link *media_entity_add_link(struct media_entity *entity)
300 {
301 if (entity->num_links >= entity->max_links) {
302 struct media_link *links = entity->links;
303 unsigned int max_links = entity->max_links * 2;
304 unsigned int i;
305
306 links = realloc(links, max_links * sizeof *links);
307 if (links == NULL)
308 return NULL;
309
310 for (i = 0; i < entity->num_links; ++i)
311 links[i].twin->twin = &links[i];
312
313 entity->max_links = max_links;
314 entity->links = links;
315 }
316
317 return &entity->links[entity->num_links++];
318 }
319
media_enum_links(struct media_device * media)320 static int media_enum_links(struct media_device *media)
321 {
322 uint32_t id;
323 int ret = 0;
324
325 for (id = 1; id <= media->entities_count; id++) {
326 struct media_entity *entity = &media->entities[id - 1];
327 struct media_links_enum links = { 0 };
328 unsigned int i;
329
330 links.entity = entity->info.id;
331 links.pads = calloc(entity->info.pads, sizeof(struct media_pad_desc));
332 links.links = calloc(entity->info.links, sizeof(struct media_link_desc));
333
334 if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) {
335 ret = -errno;
336 media_dbg(media,
337 "%s: Unable to enumerate pads and links (%s).\n",
338 __func__, strerror(errno));
339 free(links.pads);
340 free(links.links);
341 return ret;
342 }
343
344 for (i = 0; i < entity->info.pads; ++i) {
345 entity->pads[i].entity = entity;
346 entity->pads[i].index = links.pads[i].index;
347 entity->pads[i].flags = links.pads[i].flags;
348 }
349
350 for (i = 0; i < entity->info.links; ++i) {
351 struct media_link_desc *link = &links.links[i];
352 struct media_link *fwdlink;
353 struct media_link *backlink;
354 struct media_entity *source;
355 struct media_entity *sink;
356
357 source = media_get_entity_by_id(media, link->source.entity);
358 sink = media_get_entity_by_id(media, link->sink.entity);
359
360 if (source == NULL || sink == NULL) {
361 media_dbg(media,
362 "WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n",
363 id, i, link->source.entity,
364 link->source.index,
365 link->sink.entity,
366 link->sink.index);
367 ret = -EINVAL;
368 } else {
369 fwdlink = media_entity_add_link(source);
370 fwdlink->source = &source->pads[link->source.index];
371 fwdlink->sink = &sink->pads[link->sink.index];
372 fwdlink->flags = link->flags;
373
374 backlink = media_entity_add_link(sink);
375 backlink->source = &source->pads[link->source.index];
376 backlink->sink = &sink->pads[link->sink.index];
377 backlink->flags = link->flags;
378
379 fwdlink->twin = backlink;
380 backlink->twin = fwdlink;
381 }
382 }
383
384 free(links.pads);
385 free(links.links);
386 }
387
388 return ret;
389 }
390
391 #ifdef HAVE_LIBUDEV
392
393 #include <libudev.h>
394
media_udev_open(struct udev ** udev)395 static inline int media_udev_open(struct udev **udev)
396 {
397 *udev = udev_new();
398 if (*udev == NULL)
399 return -ENOMEM;
400 return 0;
401 }
402
media_udev_close(struct udev * udev)403 static inline void media_udev_close(struct udev *udev)
404 {
405 if (udev != NULL)
406 udev_unref(udev);
407 }
408
media_get_devname_udev(struct udev * udev,struct media_entity * entity)409 static int media_get_devname_udev(struct udev *udev,
410 struct media_entity *entity)
411 {
412 struct udev_device *device;
413 dev_t devnum;
414 const char *p;
415 int ret = -ENODEV;
416
417 if (udev == NULL)
418 return -EINVAL;
419
420 devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
421 media_dbg(entity->media, "looking up device: %u:%u\n",
422 major(devnum), minor(devnum));
423 device = udev_device_new_from_devnum(udev, 'c', devnum);
424 if (device) {
425 p = udev_device_get_devnode(device);
426 if (p) {
427 strncpy(entity->devname, p, sizeof(entity->devname));
428 entity->devname[sizeof(entity->devname) - 1] = '\0';
429 }
430 ret = 0;
431 }
432
433 udev_device_unref(device);
434
435 return ret;
436 }
437
438 #else /* HAVE_LIBUDEV */
439
440 struct udev;
441
media_udev_open(struct udev ** udev)442 static inline int media_udev_open(struct udev **udev) { return 0; }
443
media_udev_close(struct udev * udev)444 static inline void media_udev_close(struct udev *udev) { }
445
media_get_devname_udev(struct udev * udev,struct media_entity * entity)446 static inline int media_get_devname_udev(struct udev *udev,
447 struct media_entity *entity)
448 {
449 return -ENOTSUP;
450 }
451
452 #endif /* HAVE_LIBUDEV */
453
media_get_devname_sysfs(struct media_entity * entity)454 static int media_get_devname_sysfs(struct media_entity *entity)
455 {
456 struct stat devstat;
457 char devname[32];
458 char sysname[32];
459 char target[1024];
460 char *p;
461 int ret;
462
463 sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major,
464 entity->info.v4l.minor);
465 ret = readlink(sysname, target, sizeof(target) - 1);
466 if (ret < 0)
467 return -errno;
468
469 target[ret] = '\0';
470 p = strrchr(target, '/');
471 if (p == NULL)
472 return -EINVAL;
473
474 sprintf(devname, "/dev/%s", p + 1);
475 if (strstr(p + 1, "dvb")) {
476 char *s = p + 1;
477
478 if (strncmp(s, "dvb", 3))
479 return -EINVAL;
480 s += 3;
481 p = strchr(s, '.');
482 if (!p)
483 return -EINVAL;
484 *p = '/';
485 sprintf(devname, "/dev/dvb/adapter%s", s);
486 } else {
487 sprintf(devname, "/dev/%s", p + 1);
488 }
489 ret = stat(devname, &devstat);
490 if (ret < 0)
491 return -errno;
492
493 /* Sanity check: udev might have reordered the device nodes.
494 * Make sure the major/minor match. We should really use
495 * libudev.
496 */
497 if (major(devstat.st_rdev) == entity->info.v4l.major &&
498 minor(devstat.st_rdev) == entity->info.v4l.minor)
499 strcpy(entity->devname, devname);
500
501 return 0;
502 }
503
media_enum_entities(struct media_device * media)504 static int media_enum_entities(struct media_device *media)
505 {
506 struct media_entity *entity;
507 struct udev *udev;
508 unsigned int size;
509 uint32_t id;
510 int ret;
511
512 ret = media_udev_open(&udev);
513 if (ret < 0)
514 media_dbg(media, "Can't get udev context\n");
515
516 for (id = 0, ret = 0; ; id = entity->info.id) {
517 size = (media->entities_count + 1) * sizeof(*media->entities);
518 media->entities = realloc(media->entities, size);
519
520 entity = &media->entities[media->entities_count];
521 memset(entity, 0, sizeof(*entity));
522 entity->fd = -1;
523 entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT;
524 entity->media = media;
525
526 ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info);
527 if (ret < 0) {
528 ret = errno != EINVAL ? -errno : 0;
529 break;
530 }
531
532 /* Number of links (for outbound links) plus number of pads (for
533 * inbound links) is a good safe initial estimate of the total
534 * number of links.
535 */
536 entity->max_links = entity->info.pads + entity->info.links;
537
538 entity->pads = malloc(entity->info.pads * sizeof(*entity->pads));
539 entity->links = malloc(entity->max_links * sizeof(*entity->links));
540 if (entity->pads == NULL || entity->links == NULL) {
541 ret = -ENOMEM;
542 break;
543 }
544
545 media->entities_count++;
546
547 if (entity->info.flags & MEDIA_ENT_FL_DEFAULT) {
548 switch (entity->info.type) {
549 case MEDIA_ENT_T_DEVNODE_V4L:
550 media->def.v4l = entity;
551 break;
552 case MEDIA_ENT_T_DEVNODE_FB:
553 media->def.fb = entity;
554 break;
555 case MEDIA_ENT_T_DEVNODE_ALSA:
556 media->def.alsa = entity;
557 break;
558 case MEDIA_ENT_T_DEVNODE_DVB:
559 media->def.dvb = entity;
560 break;
561 }
562 }
563
564 /* Find the corresponding device name. */
565 if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE &&
566 media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV)
567 continue;
568
569 /* Don't try to parse empty major,minor */
570 if (!entity->info.dev.major && !entity->info.dev.minor)
571 continue;
572
573 /* Try to get the device name via udev */
574 if (!media_get_devname_udev(udev, entity))
575 continue;
576
577 /* Fall back to get the device name via sysfs */
578 media_get_devname_sysfs(entity);
579 }
580
581 media_udev_close(udev);
582 return ret;
583 }
584
media_device_enumerate(struct media_device * media)585 int media_device_enumerate(struct media_device *media)
586 {
587 int ret;
588
589 if (media->entities)
590 return 0;
591
592 ret = media_device_open(media);
593 if (ret < 0)
594 return ret;
595
596 memset(&media->info, 0, sizeof(media->info));
597
598 ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info);
599 if (ret < 0) {
600 ret = -errno;
601 media_dbg(media, "%s: Unable to retrieve media device "
602 "information for device %s (%s)\n", __func__,
603 media->devnode, strerror(errno));
604 goto done;
605 }
606
607 media_dbg(media, "Enumerating entities\n");
608
609 ret = media_enum_entities(media);
610 if (ret < 0) {
611 media_dbg(media,
612 "%s: Unable to enumerate entities for device %s (%s)\n",
613 __func__, media->devnode, strerror(-ret));
614 goto done;
615 }
616
617 media_dbg(media, "Found %u entities\n", media->entities_count);
618 media_dbg(media, "Enumerating pads and links\n");
619
620 ret = media_enum_links(media);
621 if (ret < 0) {
622 media_dbg(media,
623 "%s: Unable to enumerate pads and linksfor device %s\n",
624 __func__, media->devnode);
625 goto done;
626 }
627
628 ret = 0;
629
630 done:
631 media_device_close(media);
632 return ret;
633 }
634
635 /* -----------------------------------------------------------------------------
636 * Create/destroy
637 */
638
media_debug_default(void * ptr,...)639 static void media_debug_default(void *ptr, ...)
640 {
641 }
642
media_debug_set_handler(struct media_device * media,void (* debug_handler)(void *,...),void * debug_priv)643 void media_debug_set_handler(struct media_device *media,
644 void (*debug_handler)(void *, ...),
645 void *debug_priv)
646 {
647 if (debug_handler) {
648 media->debug_handler = debug_handler;
649 media->debug_priv = debug_priv;
650 } else {
651 media->debug_handler = media_debug_default;
652 media->debug_priv = NULL;
653 }
654 }
655
__media_device_new(void)656 static struct media_device *__media_device_new(void)
657 {
658 struct media_device *media;
659
660 media = calloc(1, sizeof(*media));
661 if (media == NULL)
662 return NULL;
663
664 media->fd = -1;
665 media->refcount = 1;
666
667 media_debug_set_handler(media, NULL, NULL);
668
669 return media;
670 }
671
media_device_new(const char * devnode)672 struct media_device *media_device_new(const char *devnode)
673 {
674 struct media_device *media;
675
676 media = __media_device_new();
677 if (media == NULL)
678 return NULL;
679
680 media->devnode = strdup(devnode);
681 if (media->devnode == NULL) {
682 media_device_unref(media);
683 return NULL;
684 }
685
686 return media;
687 }
688
media_device_new_emulated(struct media_device_info * info)689 struct media_device *media_device_new_emulated(struct media_device_info *info)
690 {
691 struct media_device *media;
692
693 media = __media_device_new();
694 if (media == NULL)
695 return NULL;
696
697 media->info = *info;
698
699 return media;
700 }
701
media_device_ref(struct media_device * media)702 struct media_device *media_device_ref(struct media_device *media)
703 {
704 media->refcount++;
705 return media;
706 }
707
media_device_unref(struct media_device * media)708 void media_device_unref(struct media_device *media)
709 {
710 unsigned int i;
711
712 media->refcount--;
713 if (media->refcount > 0)
714 return;
715
716 for (i = 0; i < media->entities_count; ++i) {
717 struct media_entity *entity = &media->entities[i];
718
719 free(entity->pads);
720 free(entity->links);
721 if (entity->fd != -1)
722 close(entity->fd);
723 }
724
725 free(media->entities);
726 free(media->devnode);
727 free(media);
728 }
729
media_device_add_entity(struct media_device * media,const struct media_entity_desc * desc,const char * devnode)730 int media_device_add_entity(struct media_device *media,
731 const struct media_entity_desc *desc,
732 const char *devnode)
733 {
734 struct media_entity **defent = NULL;
735 struct media_entity *entity;
736 unsigned int size;
737
738 size = (media->entities_count + 1) * sizeof(*media->entities);
739 entity = realloc(media->entities, size);
740 if (entity == NULL)
741 return -ENOMEM;
742
743 media->entities = entity;
744 media->entities_count++;
745
746 entity = &media->entities[media->entities_count - 1];
747 memset(entity, 0, sizeof *entity);
748
749 entity->fd = -1;
750 entity->media = media;
751 strncpy(entity->devname, devnode, sizeof entity->devname);
752 entity->devname[sizeof entity->devname - 1] = '\0';
753
754 entity->info.id = 0;
755 entity->info.type = desc->type;
756 entity->info.flags = 0;
757 memcpy(entity->info.name, desc->name, sizeof entity->info.name);
758
759 switch (entity->info.type) {
760 case MEDIA_ENT_T_DEVNODE_V4L:
761 defent = &media->def.v4l;
762 entity->info.v4l = desc->v4l;
763 break;
764 case MEDIA_ENT_T_DEVNODE_FB:
765 defent = &media->def.fb;
766 entity->info.fb = desc->fb;
767 break;
768 case MEDIA_ENT_T_DEVNODE_ALSA:
769 defent = &media->def.alsa;
770 entity->info.alsa = desc->alsa;
771 break;
772 case MEDIA_ENT_T_DEVNODE_DVB:
773 defent = &media->def.dvb;
774 entity->info.dvb = desc->dvb;
775 break;
776 }
777
778 if (desc->flags & MEDIA_ENT_FL_DEFAULT) {
779 entity->info.flags |= MEDIA_ENT_FL_DEFAULT;
780 if (defent)
781 *defent = entity;
782 }
783
784 return 0;
785 }
786
media_parse_entity(struct media_device * media,const char * p,char ** endp)787 struct media_entity *media_parse_entity(struct media_device *media,
788 const char *p, char **endp)
789 {
790 unsigned int entity_id;
791 struct media_entity *entity;
792 char *end;
793
794 /* endp can be NULL. To avoid spreading NULL checks across the function,
795 * set endp to &end in that case.
796 */
797 if (endp == NULL)
798 endp = &end;
799
800 for (; isspace(*p); ++p);
801
802 if (*p == '"' || *p == '\'') {
803 char *name;
804
805 for (end = (char *)p + 1; *end && *end != '"' && *end != '\''; ++end);
806 if (*end != '"' && *end != '\'') {
807 media_dbg(media, "missing matching '\"'\n");
808 *endp = end;
809 return NULL;
810 }
811
812 name = strndup(p + 1, end - p - 1);
813 if (!name)
814 return NULL;
815 entity = media_get_entity_by_name(media, name);
816 free(name);
817 if (entity == NULL) {
818 media_dbg(media, "no such entity \"%.*s\"\n", end - p - 1, p + 1);
819 *endp = (char *)p + 1;
820 return NULL;
821 }
822
823 ++end;
824 } else {
825 entity_id = strtoul(p, &end, 10);
826 entity = media_get_entity_by_id(media, entity_id);
827 if (entity == NULL) {
828 media_dbg(media, "no such entity %d\n", entity_id);
829 *endp = (char *)p;
830 return NULL;
831 }
832 }
833 for (p = end; isspace(*p); ++p);
834
835 *endp = (char *)p;
836
837 return entity;
838 }
839
media_parse_pad(struct media_device * media,const char * p,char ** endp)840 struct media_pad *media_parse_pad(struct media_device *media,
841 const char *p, char **endp)
842 {
843 unsigned int pad;
844 struct media_entity *entity;
845 char *end;
846
847 if (endp == NULL)
848 endp = &end;
849
850 entity = media_parse_entity(media, p, &end);
851 if (!entity) {
852 *endp = end;
853 return NULL;
854 }
855
856 if (*end != ':') {
857 media_dbg(media, "Expected ':'\n", *end);
858 *endp = end;
859 return NULL;
860 }
861
862 for (p = end + 1; isspace(*p); ++p);
863
864 pad = strtoul(p, &end, 10);
865
866 if (pad >= entity->info.pads) {
867 media_dbg(media, "No pad '%d' on entity \"%s\". Maximum pad number is %d\n",
868 pad, entity->info.name, entity->info.pads - 1);
869 *endp = (char *)p;
870 return NULL;
871 }
872
873 for (p = end; isspace(*p); ++p);
874 *endp = (char *)p;
875
876 return &entity->pads[pad];
877 }
878
media_parse_link(struct media_device * media,const char * p,char ** endp)879 struct media_link *media_parse_link(struct media_device *media,
880 const char *p, char **endp)
881 {
882 struct media_link *link;
883 struct media_pad *source;
884 struct media_pad *sink;
885 unsigned int i;
886 char *end;
887
888 source = media_parse_pad(media, p, &end);
889 if (source == NULL) {
890 *endp = end;
891 return NULL;
892 }
893
894 if (end[0] != '-' || end[1] != '>') {
895 *endp = end;
896 media_dbg(media, "Expected '->'\n");
897 return NULL;
898 }
899
900 p = end + 2;
901
902 sink = media_parse_pad(media, p, &end);
903 if (sink == NULL) {
904 *endp = end;
905 return NULL;
906 }
907
908 *endp = end;
909
910 for (i = 0; i < source->entity->num_links; i++) {
911 link = &source->entity->links[i];
912
913 if (link->source == source && link->sink == sink)
914 return link;
915 }
916
917 media_dbg(media, "No link between \"%s\":%d and \"%s\":%d\n",
918 source->entity->info.name, source->index,
919 sink->entity->info.name, sink->index);
920 return NULL;
921 }
922
media_parse_setup_link(struct media_device * media,const char * p,char ** endp)923 int media_parse_setup_link(struct media_device *media,
924 const char *p, char **endp)
925 {
926 struct media_link *link;
927 uint32_t flags;
928 char *end;
929
930 link = media_parse_link(media, p, &end);
931 if (link == NULL) {
932 media_dbg(media,
933 "%s: Unable to parse link\n", __func__);
934 *endp = end;
935 return -EINVAL;
936 }
937
938 p = end;
939 if (*p++ != '[') {
940 media_dbg(media, "Unable to parse link flags: expected '['.\n");
941 *endp = (char *)p - 1;
942 return -EINVAL;
943 }
944
945 flags = strtoul(p, &end, 10);
946 for (p = end; isspace(*p); p++);
947 if (*p++ != ']') {
948 media_dbg(media, "Unable to parse link flags: expected ']'.\n");
949 *endp = (char *)p - 1;
950 return -EINVAL;
951 }
952
953 for (; isspace(*p); p++);
954 *endp = (char *)p;
955
956 media_dbg(media,
957 "Setting up link %u:%u -> %u:%u [%u]\n",
958 link->source->entity->info.id, link->source->index,
959 link->sink->entity->info.id, link->sink->index,
960 flags);
961
962 return media_setup_link(media, link->source, link->sink, flags);
963 }
964
media_print_streampos(struct media_device * media,const char * p,const char * end)965 void media_print_streampos(struct media_device *media, const char *p,
966 const char *end)
967 {
968 int pos;
969
970 pos = end - p + 1;
971
972 if (pos < 0)
973 pos = 0;
974 if (pos > strlen(p))
975 pos = strlen(p);
976
977 media_dbg(media, "\n");
978 media_dbg(media, " %s\n", p);
979 media_dbg(media, " %*s\n", pos, "^");
980 }
981
media_parse_setup_links(struct media_device * media,const char * p)982 int media_parse_setup_links(struct media_device *media, const char *p)
983 {
984 char *end;
985 int ret;
986
987 do {
988 ret = media_parse_setup_link(media, p, &end);
989 if (ret < 0) {
990 media_print_streampos(media, p, end);
991 return ret;
992 }
993
994 p = end + 1;
995 } while (*end == ',');
996
997 return *end ? -EINVAL : 0;
998 }
999