1 /*
2 V4L2 API subdev ioctl tests.
3
4 Copyright (C) 2018 Hans Verkuil <hverkuil-cisco@xs4all.nl>
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
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
19 */
20
21 #include <map>
22 #include <set>
23
24 #include <dirent.h>
25 #include <sys/sysmacros.h>
26 #include <sys/types.h>
27
28 #include "v4l2-compliance.h"
29
testMediaDeviceInfo(struct node * node)30 int testMediaDeviceInfo(struct node *node)
31 {
32 struct media_device_info mdinfo;
33
34 memset(&mdinfo, 0xff, sizeof(mdinfo));
35 fail_on_test(doioctl(node, MEDIA_IOC_DEVICE_INFO, &mdinfo));
36 if (has_mmu)
37 fail_on_test(doioctl(node, MEDIA_IOC_DEVICE_INFO, nullptr) != EFAULT);
38 fail_on_test(check_0(mdinfo.reserved, sizeof(mdinfo.reserved)));
39 fail_on_test(check_string(mdinfo.driver, sizeof(mdinfo.driver)));
40 fail_on_test(mdinfo.model[0] && check_string(mdinfo.model, sizeof(mdinfo.model)));
41 fail_on_test(mdinfo.serial[0] && check_string(mdinfo.serial, sizeof(mdinfo.serial)));
42 if (!mdinfo.bus_info[0]) {
43 warn("empty bus_info\n");
44 } else {
45 fail_on_test(check_string(mdinfo.bus_info, sizeof(mdinfo.bus_info)));
46 // Check for valid prefixes
47 if (memcmp(mdinfo.bus_info, "usb-", 4) &&
48 memcmp(mdinfo.bus_info, "PCI:", 4) &&
49 memcmp(mdinfo.bus_info, "PCIe:", 5) &&
50 memcmp(mdinfo.bus_info, "ISA:", 4) &&
51 memcmp(mdinfo.bus_info, "I2C:", 4) &&
52 memcmp(mdinfo.bus_info, "parport", 7) &&
53 memcmp(mdinfo.bus_info, "platform:", 9) &&
54 memcmp(mdinfo.bus_info, "rmi4:", 5))
55 return fail("missing bus_info prefix ('%s')\n", mdinfo.bus_info);
56 }
57 fail_on_test(mdinfo.media_version == 0);
58 if (mdinfo.media_version != MEDIA_API_VERSION)
59 fail_on_test(mdinfo.driver_version != mdinfo.media_version);
60 node->media_version = mdinfo.media_version;
61 return 0;
62 }
63
checkDevice(__u32 major,__u32 minor,bool iface,__u32 id)64 static int checkDevice(__u32 major, __u32 minor, bool iface, __u32 id)
65 {
66 char dev_path[100];
67 fail_on_test(snprintf(dev_path, sizeof(dev_path), "/sys/dev/char/%d:%d",
68 major, minor) == -1);
69 DIR *dp = opendir(dev_path);
70 if (dp == nullptr)
71 return fail("couldn't find %s for %s %u\n",
72 dev_path, iface ? "interface" : "entity", id);
73 closedir(dp);
74 return 0;
75 }
76
77 using id_set = std::set<__u32>;
78
79 static media_v2_topology topology;
80 static media_v2_entity *v2_ents;
81 static id_set v2_entities_set;
82 static media_v2_interface *v2_ifaces;
83 static id_set v2_interfaces_set;
84 static media_v2_pad *v2_pads;
85 static id_set v2_pads_set;
86 static media_v2_link *v2_links;
87 static id_set v2_links_set;
88 static std::map<__u32, __u32> entity_num_pads;
89 static std::map<__u32, media_v2_entity *> v2_entity_map;
90 static std::set<std::string> v2_entity_names_set;
91 static std::map<__u32, media_v2_interface *> v2_iface_map;
92 static std::map<__u32, media_v2_pad *> v2_pad_map;
93 static std::set<__u64> v2_entity_pad_idx_set;
94 static unsigned num_data_links;
95
checkFunction(__u32 function,bool v2_api)96 static int checkFunction(__u32 function, bool v2_api)
97 {
98 fail_on_test(function == MEDIA_ENT_F_UNKNOWN);
99 fail_on_test((function & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_T_DEVNODE &&
100 function != MEDIA_ENT_T_DEVNODE_V4L &&
101 function != MEDIA_ENT_T_DEVNODE_UNKNOWN);
102 fail_on_test((function & MEDIA_ENT_TYPE_MASK) == MEDIA_ENT_F_OLD_SUBDEV_BASE &&
103 function > MEDIA_ENT_F_TUNER);
104 if (!v2_api)
105 return 0;
106 // The old API can return these due to a horrible workaround in
107 // media-device.c. But for the v2 API these should never be returned.
108 fail_on_test(function == MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN);
109 fail_on_test(function == MEDIA_ENT_T_DEVNODE_UNKNOWN);
110 return 0;
111 }
112
testMediaTopology(struct node * node)113 int testMediaTopology(struct node *node)
114 {
115 memset(&topology, 0xff, sizeof(topology));
116 topology.ptr_entities = 0;
117 topology.ptr_interfaces = 0;
118 topology.ptr_pads = 0;
119 topology.ptr_links = 0;
120 fail_on_test(doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology));
121 fail_on_test(!topology.num_entities);
122 fail_on_test(topology.topology_version == ~0ULL);
123 fail_on_test(topology.num_entities == ~0U);
124 fail_on_test(topology.num_interfaces == ~0U);
125 fail_on_test(topology.num_pads == ~0U);
126 fail_on_test(topology.num_links == ~0U);
127 fail_on_test(topology.reserved1);
128 fail_on_test(topology.reserved2);
129 fail_on_test(topology.reserved3);
130 fail_on_test(topology.reserved4);
131 if (has_mmu) {
132 topology.ptr_entities = 4;
133 fail_on_test(doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology) != EFAULT);
134 topology.ptr_entities = 0;
135 topology.ptr_interfaces = 4;
136 fail_on_test(topology.num_interfaces &&
137 doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology) != EFAULT);
138 topology.ptr_interfaces = 0;
139 topology.ptr_pads = 4;
140 fail_on_test(topology.num_pads &&
141 doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology) != EFAULT);
142 topology.ptr_pads = 0;
143 topology.ptr_links = 4;
144 fail_on_test(topology.num_links &&
145 doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology) != EFAULT);
146 topology.ptr_links = 0;
147 }
148 v2_ents = new media_v2_entity[topology.num_entities];
149 memset(v2_ents, 0xff, topology.num_entities * sizeof(*v2_ents));
150 topology.ptr_entities = (uintptr_t)v2_ents;
151 v2_ifaces = new media_v2_interface[topology.num_interfaces];
152 memset(v2_ifaces, 0xff, topology.num_interfaces * sizeof(*v2_ifaces));
153 topology.ptr_interfaces = (uintptr_t)v2_ifaces;
154 v2_pads = new media_v2_pad[topology.num_pads];
155 memset(v2_pads, 0xff, topology.num_pads * sizeof(*v2_pads));
156 topology.ptr_pads = (uintptr_t)v2_pads;
157 v2_links = new media_v2_link[topology.num_links];
158 memset(v2_links, 0xff, topology.num_links * sizeof(*v2_links));
159 topology.ptr_links = (uintptr_t)v2_links;
160 fail_on_test(doioctl(node, MEDIA_IOC_G_TOPOLOGY, &topology));
161 fail_on_test(v2_ents != (media_v2_entity *)topology.ptr_entities);
162 fail_on_test(v2_ifaces != (media_v2_interface *)topology.ptr_interfaces);
163 fail_on_test(v2_pads != (media_v2_pad *)topology.ptr_pads);
164 fail_on_test(v2_links != (media_v2_link *)topology.ptr_links);
165
166 for (unsigned i = 0; i < topology.num_entities; i++) {
167 media_v2_entity &ent = v2_ents[i];
168 std::string key = ent.name;
169
170 /*
171 * The v2_entity_names_set set is used to check if a specific
172 * entity name is reused, and to check if the topology matches
173 * what ENUM_ENTITIES returns.
174 *
175 * However, the size of the entity name array of media_v2_entity
176 * is 64 characters, while it is 32 for media_entity_desc.
177 *
178 * So cut off the last 32 characters before storing the key.
179 * This means that the first 31 characters of the entity name
180 * must be unique, otherwise ENUM_ENTITIES would return
181 * duplicate entity names.
182 */
183 if (key.length() >= 32)
184 key.erase(31, key.length() - 1);
185
186 if (show_info) {
187 printf("\t\tEntity: 0x%08x (Name: '%s', Function: %s",
188 ent.id, ent.name, mi_entfunction2s(ent.function).c_str());
189 if (MEDIA_V2_ENTITY_HAS_FLAGS(node->media_version) && ent.flags)
190 printf(", Flags: %s", mi_entflags2s(ent.flags).c_str());
191 printf(")\n");
192 }
193 fail_on_test(check_0(ent.reserved, sizeof(ent.reserved)));
194 fail_on_test(check_string(ent.name, sizeof(ent.name)));
195 fail_on_test(!ent.id);
196 fail_on_test(checkFunction(ent.function, true));
197 fail_on_test(v2_entities_set.find(ent.id) != v2_entities_set.end());
198 fail_on_test(v2_entity_names_set.find(key) != v2_entity_names_set.end());
199 if (!MEDIA_V2_ENTITY_HAS_FLAGS(node->media_version))
200 fail_on_test(ent.flags);
201 v2_entities_set.insert(ent.id);
202 v2_entity_names_set.insert(key);
203 v2_entity_map[ent.id] = &ent;
204 }
205 for (unsigned i = 0; i < topology.num_interfaces; i++) {
206 media_v2_interface &iface = v2_ifaces[i];
207 dev_t dev = makedev(iface.devnode.major, iface.devnode.minor);
208 std::string devpath = mi_get_devpath_from_dev_t(dev);
209
210 if (show_info)
211 printf("\t\tInterface: 0x%08x (Type: %s, DevPath: %s)\n",
212 iface.id, mi_ifacetype2s(iface.intf_type).c_str(),
213 devpath.c_str());
214 fail_on_test(devpath.empty());
215 fail_on_test(check_0(iface.reserved, sizeof(iface.reserved)));
216 fail_on_test(checkDevice(iface.devnode.major, iface.devnode.minor,
217 true, iface.id));
218 fail_on_test(!iface.id);
219 fail_on_test(!iface.intf_type);
220 fail_on_test(iface.intf_type < MEDIA_INTF_T_DVB_BASE);
221 fail_on_test(iface.intf_type > MEDIA_INTF_T_ALSA_BASE + 0xff);
222 fail_on_test(iface.flags);
223 fail_on_test(v2_interfaces_set.find(iface.id) != v2_interfaces_set.end());
224 v2_interfaces_set.insert(iface.id);
225 v2_iface_map[iface.id] = &iface;
226 }
227 for (unsigned i = 0; i < topology.num_pads; i++) {
228 media_v2_pad &pad = v2_pads[i];
229 __u32 fl = pad.flags;
230
231 if (show_info) {
232 printf("\t\tPad: 0x%08x (", pad.id);
233 if (MEDIA_V2_PAD_HAS_INDEX(node->media_version))
234 printf("%u, ", pad.index);
235 printf("%s, %s)\n",
236 v2_entity_map[pad.entity_id]->name,
237 mi_padflags2s(pad.flags).c_str());
238 }
239 fail_on_test(check_0(pad.reserved, sizeof(pad.reserved)));
240 fail_on_test(!pad.id);
241 fail_on_test(!pad.entity_id);
242 fail_on_test(v2_pads_set.find(pad.id) != v2_pads_set.end());
243 v2_pads_set.insert(pad.id);
244 fail_on_test(v2_entities_set.find(pad.entity_id) == v2_entities_set.end());
245 fail_on_test(!(fl & (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)));
246 fail_on_test((fl & (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) ==
247 (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE));
248 if (MEDIA_V2_PAD_HAS_INDEX(node->media_version)) {
249 fail_on_test(pad.index == ~0U);
250 fail_on_test(v2_entity_pad_idx_set.find((__u64)pad.entity_id << 32 | pad.index) !=
251 v2_entity_pad_idx_set.end());
252 v2_entity_pad_idx_set.insert(static_cast<__u64>(pad.entity_id) << 32 | pad.index);
253 } else {
254 fail_on_test(pad.index);
255 }
256 entity_num_pads[pad.entity_id]++;
257 v2_pad_map[pad.id] = &pad;
258 }
259
260 std::set<__u32> ents_with_intf;
261
262 for (unsigned i = 0; i < topology.num_links; i++) {
263 media_v2_link &link = v2_links[i];
264 bool is_iface = (link.flags & MEDIA_LNK_FL_LINK_TYPE) ==
265 MEDIA_LNK_FL_INTERFACE_LINK;
266 bool is_anc_link = (link.flags & MEDIA_LNK_FL_LINK_TYPE) ==
267 MEDIA_LNK_FL_ANCILLARY_LINK;
268
269 fail_on_test(check_0(link.reserved, sizeof(link.reserved)));
270 fail_on_test(!link.id);
271 fail_on_test(!link.source_id);
272 fail_on_test(!link.sink_id);
273 fail_on_test(v2_links_set.find(link.id) != v2_links_set.end());
274 v2_links_set.insert(link.id);
275 if (is_iface) {
276 fail_on_test(v2_interfaces_set.find(link.source_id) == v2_interfaces_set.end());
277 fail_on_test(v2_entities_set.find(link.sink_id) == v2_entities_set.end());
278
279 media_v2_interface &iface = *v2_iface_map[link.source_id];
280 dev_t dev = makedev(iface.devnode.major, iface.devnode.minor);
281 std::string devpath = mi_get_devpath_from_dev_t(dev);
282 media_v2_entity &ent = *v2_entity_map[link.sink_id];
283
284 if (show_info)
285 printf("\t\tInterface Link: 0x%08x (%s to %s)\n", link.id,
286 ent.name, devpath.c_str());
287 ents_with_intf.insert(ent.id);
288 } else if (is_anc_link) {
289 fail_on_test(v2_entities_set.find(link.source_id) == v2_entities_set.end());
290 fail_on_test(v2_entities_set.find(link.sink_id) == v2_entities_set.end());
291 media_v2_entity &src_ent = *v2_entity_map[link.source_id];
292 media_v2_entity &sink_ent = *v2_entity_map[link.sink_id];
293 if (show_info)
294 printf("\t\tAncillary Link: 0x%08x (%s <-> %s)\n", link.id,
295 src_ent.name, sink_ent.name);
296 } else {
297 fail_on_test(v2_pads_set.find(link.source_id) == v2_pads_set.end());
298 fail_on_test(v2_pads_set.find(link.sink_id) == v2_pads_set.end());
299 fail_on_test(link.source_id == link.sink_id);
300 fail_on_test(!(v2_pad_map[link.source_id]->flags & MEDIA_PAD_FL_SOURCE));
301 fail_on_test(!(v2_pad_map[link.sink_id]->flags & MEDIA_PAD_FL_SINK));
302 num_data_links++;
303 if (show_info)
304 printf("\t\tData Link: 0x%08x (%s:%u -> %s:%u, %s)\n", link.id,
305 v2_entity_map[v2_pad_map[link.source_id]->entity_id]->name,
306 v2_pad_map[link.source_id]->index,
307 v2_entity_map[v2_pad_map[link.sink_id]->entity_id]->name,
308 v2_pad_map[link.sink_id]->index,
309 mi_linkflags2s(link.flags).c_str());
310 }
311 }
312
313 for (unsigned i = 0; i < topology.num_entities; i++) {
314 media_v2_entity &ent = v2_ents[i];
315
316 fail_on_test(mi_func_requires_intf(ent.function) &&
317 ents_with_intf.find(ent.id) == ents_with_intf.end());
318 }
319 node->topology = &topology;
320 return 0;
321 }
322
323 static media_link_desc link_immutable;
324 static media_link_desc link_enabled;
325 static media_link_desc link_disabled;
326
testMediaEnum(struct node * node)327 int testMediaEnum(struct node *node)
328 {
329 using entity_map = std::map<__u32, media_entity_desc>;
330 entity_map ent_map;
331 id_set has_default_set;
332 struct media_entity_desc ent;
333 struct media_links_enum links;
334 unsigned num_links = 0;
335 __u32 last_id = 0;
336 int ret;
337
338 memset(&ent, 0, sizeof(ent));
339 // Entity ID 0 can never occur
340 ent.id = 0;
341 fail_on_test(doioctl(node, MEDIA_IOC_ENUM_ENTITIES, &ent) != EINVAL);
342 for (;;) {
343 memset(&ent, 0xff, sizeof(ent));
344 ent.id = last_id | MEDIA_ENT_ID_FLAG_NEXT;
345 ret = doioctl(node, MEDIA_IOC_ENUM_ENTITIES, &ent);
346 if (ret == EINVAL)
347 break;
348 dev_t dev = makedev(ent.dev.major, ent.dev.minor);
349 std::string devpath = mi_get_devpath_from_dev_t(dev);
350
351 if (show_info) {
352 printf("\t\tEntity: 0x%08x (Name: '%s', Type: %s",
353 ent.id, ent.name, mi_entfunction2s(ent.type).c_str());
354 if (ent.flags)
355 printf(", Flags: %s", mi_entflags2s(ent.flags).c_str());
356 if (!devpath.empty())
357 printf(", DevPath: %s", devpath.c_str());
358 printf(")\n");
359 }
360 fail_on_test(dev && devpath.empty());
361 fail_on_test(check_0(ent.reserved, sizeof(ent.reserved)));
362 fail_on_test(ent.id & MEDIA_ENT_ID_FLAG_NEXT);
363 fail_on_test(!ent.id);
364 fail_on_test(ent.id <= last_id);
365 last_id = ent.id;
366 fail_on_test(check_string(ent.name, sizeof(ent.name)));
367 fail_on_test(ent.revision);
368 fail_on_test(ent.group_id);
369 fail_on_test(ent.type == ~0U);
370 fail_on_test(checkFunction(ent.type, false));
371 fail_on_test(ent.flags == ~0U);
372 fail_on_test(ent.pads == 0xffff);
373 fail_on_test(ent.links == 0xffff);
374
375 if (ent.flags & MEDIA_ENT_FL_DEFAULT) {
376 fail_on_test(has_default_set.find(ent.type) != has_default_set.end());
377 has_default_set.insert(ent.type);
378 }
379 if (!(ent.flags & MEDIA_ENT_FL_CONNECTOR)) {
380 fail_on_test(ent.dev.major == ~0U);
381 fail_on_test(ent.dev.minor == ~0U);
382 if (ent.dev.major || ent.dev.minor)
383 fail_on_test(checkDevice(ent.dev.major, ent.dev.minor,
384 false, ent.id));
385 }
386 fail_on_test(doioctl(node, MEDIA_IOC_ENUM_ENTITIES, &ent));
387 num_links += ent.links;
388
389 if (node->topology) {
390 fail_on_test(v2_entities_set.find(ent.id) == v2_entities_set.end());
391 fail_on_test(v2_entity_names_set.find(ent.name) == v2_entity_names_set.end());
392 fail_on_test(entity_num_pads[ent.id] != ent.pads);
393 // Commented out due to horrible workaround in media-device.c
394 //fail_on_test(v2_entity_map[ent.id]->function != ent.type);
395 }
396 ent_map[ent.id] = ent;
397 }
398 fail_on_test(num_data_links != num_links);
399
400 for (auto & iter : ent_map) {
401 media_entity_desc &ent = iter.second;
402
403 memset(&links, 0, sizeof(links));
404 memset(&links.reserved, 0xff, sizeof(links.reserved));
405 links.entity = ent.id;
406 fail_on_test(doioctl(node, MEDIA_IOC_ENUM_LINKS, &links));
407 fail_on_test(check_0(links.reserved, sizeof(links.reserved)));
408 fail_on_test(links.entity != ent.id);
409 fail_on_test(links.pads);
410 fail_on_test(links.links);
411 if (has_mmu) {
412 links.pads = (struct media_pad_desc *)4;
413 fail_on_test(ent.pads && doioctl(node, MEDIA_IOC_ENUM_LINKS, &links) != EFAULT);
414 links.pads = nullptr;
415 links.links = (struct media_link_desc *)4;
416 fail_on_test(ent.links && doioctl(node, MEDIA_IOC_ENUM_LINKS, &links) != EFAULT);
417 links.links = nullptr;
418 }
419 links.pads = new media_pad_desc[ent.pads];
420 memset(links.pads, 0xff, ent.pads * sizeof(*links.pads));
421 links.links = new media_link_desc[ent.links];
422 memset(links.links, 0xff, ent.links * sizeof(*links.links));
423 memset(&links.reserved, 0xff, sizeof(links.reserved));
424 fail_on_test(doioctl(node, MEDIA_IOC_ENUM_LINKS, &links));
425 fail_on_test(check_0(links.reserved, sizeof(links.reserved)));
426
427 for (unsigned i = 0; i < ent.pads; i++) {
428 fail_on_test(links.pads[i].entity != ent.id);
429 fail_on_test(links.pads[i].index == 0xffff);
430 fail_on_test(check_0(links.pads[i].reserved, sizeof(links.pads[i].reserved)));
431 __u32 fl = links.pads[i].flags;
432 fail_on_test(!(fl & (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)));
433 fail_on_test((fl & (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) ==
434 (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE));
435 if (node->topology &&
436 MEDIA_V2_PAD_HAS_INDEX(node->media_version)) {
437 __u64 key = static_cast<__u64>(ent.id) << 32 | links.pads[i].index;
438
439 fail_on_test(v2_entity_pad_idx_set.find(key) ==
440 v2_entity_pad_idx_set.end());
441 v2_entity_pad_idx_set.erase(key);
442 }
443 }
444 bool found_enabled = false;
445 for (unsigned i = 0; i < ent.links; i++) {
446 bool is_sink = links.links[i].sink.entity == ent.id;
447 __u32 fl = links.links[i].flags;
448 __u32 remote_ent;
449 __u16 remote_pad;
450
451 fail_on_test(links.links[i].source.entity != ent.id &&
452 links.links[i].sink.entity != ent.id);
453 fail_on_test(check_0(links.links[i].reserved, sizeof(links.links[i].reserved)));
454 if (fl & MEDIA_LNK_FL_IMMUTABLE) {
455 fail_on_test(!(fl & MEDIA_LNK_FL_ENABLED));
456 fail_on_test(fl & MEDIA_LNK_FL_DYNAMIC);
457 if (!link_immutable.source.entity)
458 link_immutable = links.links[i];
459 }
460 if (fl & MEDIA_LNK_FL_DYNAMIC)
461 fail_on_test(fl & MEDIA_LNK_FL_IMMUTABLE);
462 if (is_sink && (fl & MEDIA_LNK_FL_ENABLED)) {
463 // only one incoming link can be enabled
464 fail_on_test(found_enabled);
465 found_enabled = true;
466 }
467 if ((fl & MEDIA_LNK_FL_ENABLED) && !link_enabled.source.entity)
468 link_enabled = links.links[i];
469 if (!(fl & (MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED)) &&
470 !link_disabled.source.entity)
471 link_disabled = links.links[i];
472
473 // This ioctl only returns data links
474 fail_on_test((fl & MEDIA_LNK_FL_LINK_TYPE) != MEDIA_LNK_FL_DATA_LINK);
475 fail_on_test(links.links[i].sink.entity == links.links[i].source.entity);
476 if (is_sink) {
477 fail_on_test(links.links[i].sink.index >= ent.pads);
478 remote_ent = links.links[i].source.entity;
479 remote_pad = links.links[i].source.index;
480 } else {
481 fail_on_test(links.links[i].source.index >= ent.pads);
482 remote_ent = links.links[i].sink.entity;
483 remote_pad = links.links[i].sink.index;
484 }
485 fail_on_test(ent_map.find(remote_ent) == ent_map.end());
486 media_entity_desc &remote = ent_map[remote_ent];
487 fail_on_test(remote_pad >= remote.pads);
488 }
489 }
490
491 memset(&links, 0, sizeof(links));
492 fail_on_test(doioctl(node, MEDIA_IOC_ENUM_LINKS, &links) != EINVAL);
493
494 for (unsigned i = 0; i < topology.num_entities; i++)
495 fail_on_test(ent_map.find(v2_ents[i].id) == ent_map.end());
496
497 if (node->topology &&
498 MEDIA_V2_PAD_HAS_INDEX(node->media_version))
499 fail_on_test(!v2_entity_pad_idx_set.empty());
500 return 0;
501 }
502
testMediaSetupLink(struct node * node)503 int testMediaSetupLink(struct node *node)
504 {
505 struct media_link_desc link;
506 int ret;
507
508 memset(&link, 0, sizeof(link));
509 ret = doioctl(node, MEDIA_IOC_SETUP_LINK, &link);
510 if (ret == ENOTTY)
511 return ret;
512 fail_on_test(ret != EINVAL);
513 if (link_immutable.source.entity) {
514 link = link_immutable;
515 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link));
516 link.flags = MEDIA_LNK_FL_ENABLED;
517 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link) != EINVAL);
518 }
519 if (link_disabled.source.entity) {
520 link = link_disabled;
521 memset(link.reserved, 0xff, sizeof(link.reserved));
522 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link));
523 fail_on_test(check_0(link.reserved, sizeof(link.reserved)));
524 link.flags |= MEDIA_LNK_FL_INTERFACE_LINK;
525 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link) != EINVAL);
526 }
527 if (link_enabled.source.entity) {
528 link = link_enabled;
529 memset(link.reserved, 0xff, sizeof(link.reserved));
530 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link));
531 fail_on_test(check_0(link.reserved, sizeof(link.reserved)));
532 link.flags |= MEDIA_LNK_FL_INTERFACE_LINK;
533 fail_on_test(doioctl(node, MEDIA_IOC_SETUP_LINK, &link) != EINVAL);
534 }
535 return 0;
536 }
537
walkTopology(struct node & node,struct node & expbuf_node,unsigned frame_count,unsigned all_fmt_frame_count)538 void walkTopology(struct node &node, struct node &expbuf_node,
539 unsigned frame_count, unsigned all_fmt_frame_count)
540 {
541 media_v2_topology topology;
542
543 memset(&topology, 0, sizeof(topology));
544 if (ioctl(node.g_fd(), MEDIA_IOC_G_TOPOLOGY, &topology))
545 return;
546
547 media_v2_interface v2_ifaces[topology.num_interfaces];
548
549 topology.ptr_interfaces = (uintptr_t)v2_ifaces;
550 if (ioctl(node.g_fd(), MEDIA_IOC_G_TOPOLOGY, &topology))
551 return;
552
553 for (unsigned i = 0; i < topology.num_interfaces; i++) {
554 media_v2_interface &iface = v2_ifaces[i];
555 std::string dev = mi_media_get_device(iface.devnode.major,
556 iface.devnode.minor);
557 if (dev.empty())
558 continue;
559
560 printf("--------------------------------------------------------------------------------\n");
561
562 media_type type = mi_media_detect_type(dev.c_str());
563 if (type == MEDIA_TYPE_CANT_STAT) {
564 fprintf(stderr, "\nCannot open device %s, skipping.\n\n",
565 dev.c_str());
566 continue;
567 }
568
569 switch (type) {
570 // For now we can only handle V4L2 devices
571 case MEDIA_TYPE_VIDEO:
572 case MEDIA_TYPE_VBI:
573 case MEDIA_TYPE_RADIO:
574 case MEDIA_TYPE_SDR:
575 case MEDIA_TYPE_TOUCH:
576 case MEDIA_TYPE_SUBDEV:
577 break;
578 default:
579 type = MEDIA_TYPE_UNKNOWN;
580 break;
581 }
582
583 if (type == MEDIA_TYPE_UNKNOWN) {
584 fprintf(stderr, "\nUnable to detect what device %s is, skipping.\n\n",
585 dev.c_str());
586 continue;
587 }
588
589 struct node test_node;
590 int fd = -1;
591
592 test_node.device = dev.c_str();
593 test_node.s_trace(node.g_trace());
594 switch (type) {
595 case MEDIA_TYPE_MEDIA:
596 test_node.s_direct(true);
597 fd = test_node.media_open(dev.c_str(), false);
598 break;
599 case MEDIA_TYPE_SUBDEV:
600 test_node.s_direct(true);
601 fd = test_node.subdev_open(dev.c_str(), false);
602 break;
603 default:
604 test_node.s_direct(node.g_direct());
605 fd = test_node.open(dev.c_str(), false);
606 break;
607 }
608 if (fd < 0) {
609 fprintf(stderr, "\nFailed to open device %s, skipping\n\n",
610 dev.c_str());
611 continue;
612 }
613
614 testNode(test_node, test_node, expbuf_node, type,
615 frame_count, all_fmt_frame_count, node.g_fd());
616 test_node.close();
617 }
618 }
619