• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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