1 // SPDX-License-Identifier: LGPL-2.1-only
2 /*
3 * Copyright 2018 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4 */
5
6 #include <cstdint>
7 #include <cstring>
8 #include <fstream>
9 #include <iostream>
10
11 #include <dirent.h>
12 #include <fcntl.h>
13 #include <sys/ioctl.h>
14 #include <sys/stat.h>
15 #include <sys/sysmacros.h>
16 #include <unistd.h>
17
18 #include <linux/media.h>
19
20 #include <media-info.h>
21 #ifndef __linux__
22 #include <linux/videodev2.h>
23 #include <fcntl.h>
24 #endif
25
num2s(unsigned num,bool is_hex=true)26 static std::string num2s(unsigned num, bool is_hex = true)
27 {
28 char buf[16];
29
30 if (is_hex)
31 sprintf(buf, "%08x", num);
32 else
33 sprintf(buf, "%u", num);
34 return buf;
35 }
36
37 static constexpr struct {
38 const char *devname;
39 enum media_type type;
40 } media_types[] = {
41 { "video", MEDIA_TYPE_VIDEO },
42 { "vbi", MEDIA_TYPE_VBI },
43 { "radio", MEDIA_TYPE_RADIO },
44 { "swradio", MEDIA_TYPE_SDR },
45 { "v4l-subdev", MEDIA_TYPE_SUBDEV },
46 { "v4l-touch", MEDIA_TYPE_TOUCH },
47 { "media", MEDIA_TYPE_MEDIA },
48 { "frontend", MEDIA_TYPE_DVB_FRONTEND },
49 { "demux", MEDIA_TYPE_DVB_DEMUX },
50 { "dvr", MEDIA_TYPE_DVB_DVR },
51 { "net", MEDIA_TYPE_DVB_NET },
52 { "ca", MEDIA_TYPE_DTV_CA },
53 { nullptr, MEDIA_TYPE_UNKNOWN }
54 };
55
mi_media_detect_type(const char * device)56 media_type mi_media_detect_type(const char *device)
57 {
58 struct stat sb;
59
60 if (stat(device, &sb) == -1)
61 return MEDIA_TYPE_CANT_STAT;
62 #ifdef __linux__
63 std::string uevent_path("/sys/dev/char/");
64
65 uevent_path += num2s(major(sb.st_rdev), false) + ":" +
66 num2s(minor(sb.st_rdev), false) + "/uevent";
67
68 std::ifstream uevent_file(uevent_path.c_str());
69 if (uevent_file.fail())
70 return MEDIA_TYPE_UNKNOWN;
71
72 std::string line;
73
74 while (std::getline(uevent_file, line)) {
75 if (line.compare(0, 8, "DEVNAME="))
76 continue;
77
78 line.erase(0, 8);
79 if (!line.compare(0, 11, "dvb/adapter")) {
80 line.erase(0, 11);
81 unsigned len = 0;
82 while (line[len] && line[len] != '/')
83 len++;
84 line.erase(0, len + 1);
85 }
86 for (size_t i = 0; media_types[i].devname; i++) {
87 const char *devname = media_types[i].devname;
88 size_t len = strlen(devname);
89
90 if (!line.compare(0, len, devname) && isdigit(line[0+len])) {
91 uevent_file.close();
92 return media_types[i].type;
93 }
94 }
95 }
96
97 uevent_file.close();
98 #else // Not Linux
99 int fd = open(device, O_RDONLY);
100 if (fd >= 0) {
101 struct v4l2_capability caps;
102 int error = ioctl(fd, VIDIOC_QUERYCAP, &caps);
103 close(fd);
104 if (error == 0) {
105 if (caps.device_caps & V4L2_CAP_VIDEO_CAPTURE) {
106 return MEDIA_TYPE_VIDEO;
107 } else if (caps.device_caps & V4L2_CAP_VBI_CAPTURE) {
108 return MEDIA_TYPE_VBI;
109 } else if (caps.device_caps & V4L2_CAP_RADIO) {
110 return MEDIA_TYPE_RADIO;
111 }
112 }
113 }
114 #endif
115 return MEDIA_TYPE_UNKNOWN;
116 }
117
mi_media_get_device(__u32 major,__u32 minor)118 std::string mi_media_get_device(__u32 major, __u32 minor)
119 {
120 char fmt[32];
121 std::string uevent_path("/sys/dev/char/");
122
123 sprintf(fmt, "%d:%d", major, minor);
124 uevent_path += std::string(fmt) + "/uevent";
125
126 std::ifstream uevent_file(uevent_path.c_str());
127 if (uevent_file.fail())
128 return "";
129
130 std::string line;
131
132 while (std::getline(uevent_file, line)) {
133 if (line.compare(0, 8, "DEVNAME="))
134 continue;
135 uevent_file.close();
136 return std::string("/dev/") + &line[8];
137 }
138
139 uevent_file.close();
140 return "";
141 }
142
143 struct flag_def {
144 unsigned flag;
145 const char *str;
146 };
147
flags2s(unsigned val,const flag_def * def)148 static std::string flags2s(unsigned val, const flag_def *def)
149 {
150 std::string s;
151
152 while (def->flag) {
153 if (val & def->flag) {
154 if (s.length()) s += ", ";
155 s += def->str;
156 val &= ~def->flag;
157 }
158 def++;
159 }
160 if (val) {
161 if (s.length()) s += ", ";
162 s += num2s(val);
163 }
164 return s;
165 }
166
mi_get_dev_t_from_fd(int fd,dev_t * dev)167 int mi_get_dev_t_from_fd(int fd, dev_t *dev)
168 {
169 struct stat sb;
170
171 if (fstat(fd, &sb) == -1) {
172 fprintf(stderr, "failed to stat file\n");
173 return -1;
174 }
175 *dev = sb.st_rdev;
176 return 0;
177 }
178
mi_get_devpath_from_dev_t(dev_t dev)179 std::string mi_get_devpath_from_dev_t(dev_t dev)
180 {
181 if (!dev)
182 return "";
183
184 std::string media_uevent_path("/sys/dev/char/");
185
186 media_uevent_path += num2s(major(dev), false) + ":" +
187 num2s(minor(dev), false) + "/uevent";
188
189 FILE *uevent_fd = fopen(media_uevent_path.c_str(), "r");
190
191 if (uevent_fd == nullptr) {
192 fprintf(stderr, "failed to open %s\n", media_uevent_path.c_str());
193 return "";
194 }
195
196 char *line = nullptr;
197 size_t size = 0;
198 std::string devpath;
199
200 for (;;) {
201 ssize_t bytes = getline(&line, &size, uevent_fd);
202
203 if (bytes <= 0)
204 break;
205 line[bytes - 1] = 0;
206 if (memcmp(line, "DEVNAME=", 8) || !line[8])
207 continue;
208 devpath = "/dev/";
209 devpath += line + 8;
210 break;
211 }
212 free(line);
213
214 if (devpath.empty())
215 fprintf(stderr, "failed to find DEVNAME in %s\n", media_uevent_path.c_str());
216 return devpath;
217 }
218
mi_get_media_fd(int fd,const char * bus_info)219 int mi_get_media_fd(int fd, const char *bus_info)
220 {
221 int media_fd = -1;
222 dev_t dev;
223
224 if (mi_get_dev_t_from_fd(fd, &dev) < 0)
225 return -1;
226
227 std::string media_path("/sys/dev/char/");
228
229 media_path += num2s(major(dev), false) + ":" +
230 num2s(minor(dev), false) + "/device";
231
232 DIR *dp;
233 struct dirent *ep;
234 dp = opendir(media_path.c_str());
235 if (dp == nullptr)
236 return -1;
237 media_path[0] = 0;
238 while ((ep = readdir(dp))) {
239 if (!memcmp(ep->d_name, "media", 5) && isdigit(ep->d_name[5])) {
240 struct media_device_info mdinfo;
241 std::string devname("/dev/");
242
243 devname += ep->d_name;
244 media_fd = open(devname.c_str(), O_RDWR);
245
246 if (bus_info &&
247 (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &mdinfo) ||
248 strcmp(mdinfo.bus_info, bus_info))) {
249 close(media_fd);
250 continue;
251 }
252 break;
253 }
254 }
255 closedir(dp);
256 return media_fd;
257 }
258
259 static constexpr flag_def entity_flags_def[] = {
260 { MEDIA_ENT_FL_DEFAULT, "default" },
261 { MEDIA_ENT_FL_CONNECTOR, "connector" },
262 { 0, nullptr }
263 };
264
mi_entflags2s(__u32 flags)265 std::string mi_entflags2s(__u32 flags)
266 {
267 return flags2s(flags, entity_flags_def);
268 }
269
270 static constexpr flag_def interface_types_def[] = {
271 { MEDIA_INTF_T_DVB_FE, "DVB Front End" },
272 { MEDIA_INTF_T_DVB_DEMUX, "DVB Demuxer" },
273 { MEDIA_INTF_T_DVB_DVR, "DVB DVR" },
274 { MEDIA_INTF_T_DVB_CA, "DVB Conditional Access" },
275 { MEDIA_INTF_T_DVB_NET, "DVB Net" },
276
277 { MEDIA_INTF_T_V4L_VIDEO, "V4L Video" },
278 { MEDIA_INTF_T_V4L_VBI, "V4L VBI" },
279 { MEDIA_INTF_T_V4L_RADIO, "V4L Radio" },
280 { MEDIA_INTF_T_V4L_SUBDEV, "V4L Sub-Device" },
281 { MEDIA_INTF_T_V4L_SWRADIO, "V4L Software Defined Radio" },
282 { MEDIA_INTF_T_V4L_TOUCH, "V4L Touch" },
283
284 { MEDIA_INTF_T_ALSA_PCM_CAPTURE, "ALSA PCM Capture" },
285 { MEDIA_INTF_T_ALSA_PCM_PLAYBACK, "ALSA PCM Playback" },
286 { MEDIA_INTF_T_ALSA_CONTROL, "ALSA Control" },
287 { MEDIA_INTF_T_ALSA_COMPRESS, "ALSA Compress" },
288 { MEDIA_INTF_T_ALSA_RAWMIDI, "ALSA Raw MIDI" },
289 { MEDIA_INTF_T_ALSA_HWDEP, "ALSA HWDEP" },
290 { MEDIA_INTF_T_ALSA_SEQUENCER, "ALSA Sequencer" },
291 { MEDIA_INTF_T_ALSA_TIMER, "ALSA Timer" },
292 { 0, nullptr }
293 };
294
mi_ifacetype2s(__u32 type)295 std::string mi_ifacetype2s(__u32 type)
296 {
297 for (unsigned i = 0; interface_types_def[i].str; i++)
298 if (type == interface_types_def[i].flag)
299 return interface_types_def[i].str;
300 return "FAIL: Unknown (" + num2s(type) + ")";
301 }
302
303 static constexpr flag_def entity_functions_def[] = {
304 { MEDIA_ENT_F_UNKNOWN, "FAIL: Uninitialized Function" },
305 { MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN, "FAIL: Unknown V4L2 Sub-Device" },
306 { MEDIA_ENT_T_DEVNODE_UNKNOWN, "FAIL: Unknown Device Node" },
307
308 { MEDIA_ENT_F_DTV_DEMOD, "Digital TV Demodulator" },
309 { MEDIA_ENT_F_TS_DEMUX, "Transport Stream Demuxer" },
310 { MEDIA_ENT_F_DTV_CA, "Digital TV Conditional Access" },
311 { MEDIA_ENT_F_DTV_NET_DECAP, "Digital TV Network ULE/MLE Desencapsulation" },
312 { MEDIA_ENT_F_IO_DTV, "Digital TV I/O" },
313 { MEDIA_ENT_F_IO_V4L, "V4L2 I/O" },
314 { MEDIA_ENT_F_IO_VBI, "VBI I/O" },
315 { MEDIA_ENT_F_IO_SWRADIO, "Software Radio I/O" },
316
317 { MEDIA_ENT_F_CAM_SENSOR, "Camera Sensor" },
318 { MEDIA_ENT_F_FLASH, "Flash Controller" },
319 { MEDIA_ENT_F_LENS, "Lens Controller" },
320 { MEDIA_ENT_F_ATV_DECODER, "Analog Video Decoder" },
321 { MEDIA_ENT_F_DV_DECODER, "Digital Video Decoder" },
322 { MEDIA_ENT_F_DV_ENCODER, "Digital Video Encoder" },
323 { MEDIA_ENT_F_TUNER, "Tuner" },
324 { MEDIA_ENT_F_IF_VID_DECODER, "IF-PLL Video Decoder" },
325 { MEDIA_ENT_F_IF_AUD_DECODER, "IF-PLL Audio Decoder" },
326 { MEDIA_ENT_F_AUDIO_CAPTURE, "Audio Capture" },
327 { MEDIA_ENT_F_AUDIO_PLAYBACK, "Audio Playback" },
328 { MEDIA_ENT_F_AUDIO_MIXER, "Audio Mixer" },
329 { MEDIA_ENT_F_PROC_VIDEO_COMPOSER, "Video Composer" },
330 { MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER, "Video Pixel Formatter" },
331 { MEDIA_ENT_F_PROC_VIDEO_PIXEL_ENC_CONV, "Video Pixel Encoding Converter" },
332 { MEDIA_ENT_F_PROC_VIDEO_LUT, "Video Look-Up Table" },
333 { MEDIA_ENT_F_PROC_VIDEO_SCALER, "Video Scaler" },
334 { MEDIA_ENT_F_PROC_VIDEO_STATISTICS, "Video Statistics" },
335 { MEDIA_ENT_F_PROC_VIDEO_DECODER, "Video Decoder" },
336 { MEDIA_ENT_F_PROC_VIDEO_ENCODER, "Video Encoder" },
337 { MEDIA_ENT_F_PROC_VIDEO_ISP, "Image Signal Processor" },
338 { MEDIA_ENT_F_VID_MUX, "Video Muxer" },
339 { MEDIA_ENT_F_VID_IF_BRIDGE, "Video Interface Bridge" },
340 { 0, nullptr }
341 };
342
mi_entfunction2s(__u32 function,bool * is_invalid)343 std::string mi_entfunction2s(__u32 function, bool *is_invalid)
344 {
345 std::string s;
346
347 if (function != MEDIA_ENT_T_DEVNODE_UNKNOWN &&
348 (function & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_F_OLD_BASE &&
349 function > MEDIA_ENT_T_DEVNODE_DVB) {
350 s = "Unknown device node (" + num2s(function) + ")";
351 if (!is_invalid)
352 return s;
353 *is_invalid = true;
354 return "FAIL: " + s;
355 }
356 if (function != MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN &&
357 (function & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_F_OLD_SUBDEV_BASE &&
358 function > MEDIA_ENT_F_TUNER) {
359 s = "Unknown sub-device (" + num2s(function) + ")";
360 if (!is_invalid)
361 return s;
362 *is_invalid = true;
363 return "FAIL: " + s;
364 }
365
366 for (unsigned i = 0; entity_functions_def[i].str; i++) {
367 if (function == entity_functions_def[i].flag) {
368 bool fail = !memcmp(entity_functions_def[i].str, "FAIL: ", 6);
369
370 if (is_invalid && fail) {
371 *is_invalid = fail;
372 return entity_functions_def[i].str;
373 }
374 return fail ? entity_functions_def[i].str + 6 : entity_functions_def[i].str;
375 }
376 }
377 if (is_invalid) {
378 *is_invalid = true;
379 return "FAIL: Unknown Function (" + num2s(function) + "), is v4l2-compliance out-of-date?";
380 }
381 return "Unknown Function (" + num2s(function) + ")";
382 }
383
mi_func_requires_intf(__u32 function)384 bool mi_func_requires_intf(__u32 function)
385 {
386 switch (function) {
387 case MEDIA_ENT_F_DTV_DEMOD:
388 case MEDIA_ENT_F_TS_DEMUX:
389 case MEDIA_ENT_F_DTV_CA:
390 case MEDIA_ENT_F_IO_V4L:
391 case MEDIA_ENT_F_IO_VBI:
392 case MEDIA_ENT_F_IO_SWRADIO:
393 return true;
394 default:
395 return false;
396 }
397 }
398
399 static constexpr flag_def pad_flags_def[] = {
400 { MEDIA_PAD_FL_SINK, "Sink" },
401 { MEDIA_PAD_FL_SOURCE, "Source" },
402 { MEDIA_PAD_FL_MUST_CONNECT, "Must Connect" },
403 { 0, nullptr }
404 };
405
mi_padflags2s(__u32 flags)406 std::string mi_padflags2s(__u32 flags)
407 {
408 return flags2s(flags, pad_flags_def);
409 }
410
411 static constexpr flag_def link_flags_def[] = {
412 { MEDIA_LNK_FL_ENABLED, "Enabled" },
413 { MEDIA_LNK_FL_IMMUTABLE, "Immutable" },
414 { MEDIA_LNK_FL_DYNAMIC, "Dynamic" },
415 { 0, nullptr }
416 };
417
mi_linkflags2s(__u32 flags)418 std::string mi_linkflags2s(__u32 flags)
419 {
420 std::string s = flags2s(flags & ~MEDIA_LNK_FL_LINK_TYPE, link_flags_def);
421
422 if (!s.empty())
423 s = ", " + s;
424 switch (flags & MEDIA_LNK_FL_LINK_TYPE) {
425 case MEDIA_LNK_FL_DATA_LINK:
426 return "Data" + s;
427 case MEDIA_LNK_FL_INTERFACE_LINK:
428 return "Interface" + s;
429 case MEDIA_LNK_FL_ANCILLARY_LINK:
430 return "Ancillary" + s;
431 default:
432 return "Unknown (" + num2s(flags) + ")" + s;
433 }
434 }
435
read_topology(int media_fd,__u32 major,__u32 minor,__u32 media_version,bool * is_invalid,__u32 * function)436 static __u32 read_topology(int media_fd, __u32 major, __u32 minor,
437 __u32 media_version, bool *is_invalid,
438 __u32 *function)
439 {
440 media_v2_topology topology;
441 unsigned i, j;
442
443 memset(&topology, 0, sizeof(topology));
444 if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology))
445 return 0;
446
447 media_v2_entity v2_ents[topology.num_entities];
448 media_v2_interface v2_ifaces[topology.num_interfaces];
449 media_v2_pad v2_pads[topology.num_pads];
450 media_v2_link v2_links[topology.num_links];
451
452 topology.ptr_entities = (uintptr_t)v2_ents;
453 topology.ptr_interfaces = (uintptr_t)v2_ifaces;
454 topology.ptr_pads = (uintptr_t)v2_pads;
455 topology.ptr_links = (uintptr_t)v2_links;
456 if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology))
457 return 0;
458 for (i = 0; i < topology.num_interfaces; i++)
459 if (v2_ifaces[i].devnode.major == major &&
460 v2_ifaces[i].devnode.minor == minor)
461 break;
462 if (i == topology.num_interfaces) {
463 fprintf(stderr, "FAIL: could not find device %d:%d in topology\n",
464 major, minor);
465 if (is_invalid)
466 *is_invalid = true;
467 if (function)
468 *function = MEDIA_ENT_F_UNKNOWN;
469 return MEDIA_ENT_F_UNKNOWN;
470 }
471 const media_v2_interface &iface = v2_ifaces[i];
472 for (i = 0; i < topology.num_links; i++) {
473 __u32 type = v2_links[i].flags & MEDIA_LNK_FL_LINK_TYPE;
474
475 if (type != MEDIA_LNK_FL_INTERFACE_LINK)
476 continue;
477 if (v2_links[i].source_id == iface.id)
478 break;
479 }
480 bool is_radio = iface.intf_type == MEDIA_INTF_T_V4L_RADIO;
481
482 if (is_radio && i < topology.num_links) {
483 if (is_invalid)
484 *is_invalid = true;
485 fprintf(stderr, "FAIL: unexpectedly found link for radio interface 0x%08x in topology\n",
486 iface.id);
487 return MEDIA_ENT_F_UNKNOWN;
488 }
489 if (!is_radio) {
490 if (i == topology.num_links) {
491 if (is_invalid)
492 *is_invalid = true;
493 fprintf(stderr, "FAIL: could not find link for interface 0x%08x in topology\n",
494 iface.id);
495 return MEDIA_ENT_F_UNKNOWN;
496 }
497 __u32 ent_id = v2_links[i].sink_id;
498 for (i = 0; i < topology.num_entities; i++) {
499 if (v2_ents[i].id == ent_id)
500 break;
501 }
502 if (i == topology.num_entities) {
503 if (is_invalid)
504 *is_invalid = true;
505 fprintf(stderr, "FAIL: could not find entity 0x%08x in topology\n",
506 ent_id);
507 return MEDIA_ENT_F_UNKNOWN;
508 }
509 }
510
511 printf("Interface Info:\n");
512 printf("\tID : 0x%08x\n", iface.id);
513 printf("\tType : %s\n", mi_ifacetype2s(iface.intf_type).c_str());
514
515 if (is_radio)
516 return MEDIA_ENT_F_UNKNOWN;
517
518 const media_v2_entity &ent = v2_ents[i];
519 printf("Entity Info:\n");
520 printf("\tID : 0x%08x (%u)\n", ent.id, ent.id);
521 printf("\tName : %s\n", ent.name);
522 printf("\tFunction : %s\n", mi_entfunction2s(ent.function, is_invalid).c_str());
523 if (MEDIA_V2_ENTITY_HAS_FLAGS(media_version) && ent.flags)
524 printf("\tFlags : %s\n", mi_entflags2s(ent.flags).c_str());
525
526 // Yes, I know, lots of nested for-loops. If we get really complex
527 // devices with such large topologies that this becomes too inefficient
528 // then this needs to be changed and we'd need some maps to quickly
529 // look up values.
530 for (i = 0; i < topology.num_pads; i++) {
531 const media_v2_pad &pad = v2_pads[i];
532
533 if (pad.entity_id != ent.id)
534 continue;
535 printf("\tPad 0x%08x : ", pad.id);
536 if (MEDIA_V2_PAD_HAS_INDEX(media_version))
537 printf("%u: ", pad.index);
538 printf("%s\n", mi_padflags2s(pad.flags).c_str());
539 for (j = 0; j < topology.num_links; j++) {
540 const media_v2_link &link = v2_links[j];
541 __u32 type = link.flags & MEDIA_LNK_FL_LINK_TYPE;
542 __u32 remote_pad;
543 __u32 remote_ent_id = 0;
544 const media_v2_entity *remote_ent = nullptr;
545
546 if (type != MEDIA_LNK_FL_DATA_LINK ||
547 (link.source_id != pad.id && link.sink_id != pad.id))
548 continue;
549 bool is_sink = link.sink_id == pad.id;
550 remote_pad = is_sink ? link.source_id : link.sink_id;
551 for (unsigned k = 0; k < topology.num_pads; k++)
552 if (v2_pads[k].id == remote_pad) {
553 remote_ent_id = v2_pads[k].entity_id;
554 break;
555 }
556 if (!remote_ent_id) {
557 if (is_invalid)
558 *is_invalid = true;
559 fprintf(stderr, "FAIL: could not find remote pad 0x%08x in topology\n",
560 remote_pad);
561 return ent.id;
562 }
563 for (unsigned k = 0; k < topology.num_entities; k++)
564 if (v2_ents[k].id == remote_ent_id) {
565 remote_ent = &v2_ents[k];
566 break;
567 }
568 if (!remote_ent) {
569 if (is_invalid)
570 *is_invalid = true;
571 fprintf(stderr, "FAIL: could not find remote entity 0x%08x in topology\n",
572 remote_ent_id);
573 return ent.id;
574 }
575 printf("\t Link 0x%08x: %s remote pad 0x%x of entity '%s' (%s): %s\n",
576 link.id, is_sink ? "from" : "to", remote_pad,
577 remote_ent->name, mi_entfunction2s(remote_ent->function, is_invalid).c_str(),
578 mi_linkflags2s(link.flags).c_str());
579 if (function && !*function)
580 *function = remote_ent->function;
581 }
582 }
583 return ent.id;
584 }
585
mi_media_info_for_fd(int media_fd,int fd,bool * is_invalid,__u32 * function)586 __u32 mi_media_info_for_fd(int media_fd, int fd, bool *is_invalid, __u32 *function)
587 {
588 struct media_device_info mdinfo;
589 struct stat sb;
590 __u32 ent_id = 0;
591
592 if (is_invalid)
593 *is_invalid = false;
594
595 if (function)
596 *function = MEDIA_ENT_F_UNKNOWN;
597
598 if (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &mdinfo))
599 return 0;
600
601 struct media_entity_desc ent;
602 bool found = false;
603
604 printf("Media Driver Info:\n");
605 printf("\tDriver name : %s\n", mdinfo.driver);
606 printf("\tModel : %s\n", mdinfo.model);
607 printf("\tSerial : %s\n", mdinfo.serial);
608 printf("\tBus info : %s\n", mdinfo.bus_info);
609 printf("\tMedia version : %d.%d.%d\n",
610 mdinfo.media_version >> 16,
611 (mdinfo.media_version >> 8) & 0xff,
612 mdinfo.media_version & 0xff);
613 printf("\tHardware revision: 0x%08x (%u)\n",
614 mdinfo.hw_revision, mdinfo.hw_revision);
615 printf("\tDriver version : %d.%d.%d\n",
616 mdinfo.driver_version >> 16,
617 (mdinfo.driver_version >> 8) & 0xff,
618 mdinfo.driver_version & 0xff);
619
620 if (fd < 0)
621 return 0;
622
623 if (fstat(fd, &sb) == -1) {
624 fprintf(stderr, "failed to stat file\n");
625 std::exit(EXIT_FAILURE);
626 }
627
628 ent_id = read_topology(media_fd, major(sb.st_rdev), minor(sb.st_rdev),
629 mdinfo.media_version, is_invalid, function);
630 if (ent_id)
631 return ent_id;
632
633 memset(&ent, 0, sizeof(ent));
634 ent.id = MEDIA_ENT_ID_FLAG_NEXT;
635 while (!ioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &ent)) {
636 if (ent.dev.major == major(sb.st_rdev) &&
637 ent.dev.minor == minor(sb.st_rdev)) {
638 found = true;
639 break;
640 }
641 ent.id |= MEDIA_ENT_ID_FLAG_NEXT;
642 }
643 if (!found)
644 return 0;
645
646 printf("Entity Info:\n");
647 printf("\tID : %u\n", ent.id);
648 printf("\tName : %s\n", ent.name);
649 printf("\tType : %s\n", mi_entfunction2s(ent.type).c_str());
650 if (ent.flags)
651 printf("\tFlags : %s\n", mi_entflags2s(ent.flags).c_str());
652 if (ent.flags & MEDIA_ENT_FL_DEFAULT) {
653 printf("\tMajor : %u\n", ent.dev.major);
654 printf("\tMinor : %u\n", ent.dev.minor);
655 }
656
657 struct media_links_enum links_enum;
658 struct media_pad_desc pads[ent.pads];
659 struct media_link_desc links[ent.links];
660
661 memset(&links_enum, 0, sizeof(links_enum));
662 links_enum.entity = ent.id;
663 links_enum.pads = pads;
664 links_enum.links = links;
665 if (ioctl(media_fd, MEDIA_IOC_ENUM_LINKS, &links_enum))
666 return ent.id;
667
668 for (unsigned i = 0; i < ent.pads; i++)
669 printf("\tPad : %u: %s\n", pads[i].index,
670 mi_padflags2s(pads[i].flags).c_str());
671 for (unsigned i = 0; i < ent.links; i++)
672 printf("\tLink : %u->%u: %s\n",
673 links[i].source.entity,
674 links[i].sink.entity,
675 mi_linkflags2s(links[i].flags).c_str());
676 return ent.id;
677 }
678