1 /*
2  * Copyright © 2016 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  * Authors:
24  *  Lyude Paul <lyude@redhat.com>
25  */
26 
27 #include "config.h"
28 
29 #include <string.h>
30 #include <errno.h>
31 #include <math.h>
32 #include <xmlrpc-c/base.h>
33 #include <xmlrpc-c/client.h>
34 #include <pthread.h>
35 #include <glib.h>
36 #include <pixman.h>
37 #include <cairo.h>
38 
39 #include "igt_chamelium.h"
40 #include "igt_core.h"
41 #include "igt_aux.h"
42 #include "igt_edid.h"
43 #include "igt_frame.h"
44 #include "igt_list.h"
45 #include "igt_kms.h"
46 #include "igt_rc.h"
47 
48 /**
49  * SECTION:igt_chamelium
50  * @short_description: Library for using the Chamelium into igt tests
51  * @title: Chamelium
52  * @include: igt_chamelium.h
53  *
54  * This library contains helpers for using Chameliums in IGT tests. This allows
55  * for tests to simulate more difficult tasks to automate such as display
56  * hotplugging, faulty display behaviors, etc.
57  *
58  * More information on the Chamelium can be found
59  * [on the ChromeOS project page](https://www.chromium.org/chromium-os/testing/chamelium).
60  *
61  * In order to run tests using the Chamelium, a valid configuration file must be
62  * present. It must contain Chamelium-specific keys as shown with the following
63  * example:
64  *
65  * |[<!-- language="plain" -->
66  *	[Chamelium]
67  *	URL=http://chameleon:9992 # The URL used for connecting to the Chamelium's RPC server
68  *
69  *	# The rest of the sections are used for defining connector mappings.
70  *	# This is required so any tests using the Chamelium know which connector
71  *	# on the test machine should be connected to each Chamelium port.
72  *	#
73  *	# In the event that any of these mappings are specified incorrectly,
74  *	# any hotplugging tests for the incorrect connector mapping will fail.
75  *
76  *	[Chamelium:DP-1] # The name of the DRM connector
77  *	ChameliumPortID=1 # The ID of the port on the Chamelium this connector is attached to
78  *
79  *	[Chamelium:HDMI-A-1]
80  *	ChameliumPortID=3
81  * ]|
82  *
83  */
84 
85 struct chamelium_edid {
86 	struct chamelium *chamelium;
87 	struct edid *base;
88 	struct edid *raw[CHAMELIUM_MAX_PORTS];
89 	int ids[CHAMELIUM_MAX_PORTS];
90 	struct igt_list link;
91 };
92 
93 struct chamelium_port {
94 	unsigned int type;
95 	int id;
96 	int connector_id;
97 	char *name;
98 };
99 
100 struct chamelium_frame_dump {
101 	unsigned char *bgr;
102 	size_t size;
103 	int width;
104 	int height;
105 	struct chamelium_port *port;
106 };
107 
108 struct chamelium_fb_crc_async_data {
109 	cairo_surface_t *fb_surface;
110 
111 	pthread_t thread_id;
112 	igt_crc_t *ret;
113 };
114 
115 struct chamelium {
116 	xmlrpc_env env;
117 	xmlrpc_client *client;
118 	char *url;
119 
120 	/* Indicates the last port to have been used for capturing video */
121 	struct chamelium_port *capturing_port;
122 
123 	int drm_fd;
124 
125 	struct igt_list edids;
126 	struct chamelium_port ports[CHAMELIUM_MAX_PORTS];
127 	int port_count;
128 };
129 
130 static struct chamelium *cleanup_instance;
131 
132 static void chamelium_do_calculate_fb_crc(cairo_surface_t *fb_surface,
133 					  igt_crc_t *out);
134 
135 /**
136  * chamelium_get_ports:
137  * @chamelium: The Chamelium instance to use
138  * @count: Where to store the number of ports
139  *
140  * Retrieves all of the ports currently configured for use with this chamelium
141  *
142  * Returns: an array containing a pointer to each configured chamelium port
143  */
chamelium_get_ports(struct chamelium * chamelium,int * count)144 struct chamelium_port **chamelium_get_ports(struct chamelium *chamelium,
145 					    int *count)
146 {
147 	int i;
148 	struct chamelium_port **ret =
149 		calloc(sizeof(void*), chamelium->port_count);
150 
151 	*count = chamelium->port_count;
152 	for (i = 0; i < chamelium->port_count; i++)
153 		ret[i] = &chamelium->ports[i];
154 
155 	return ret;
156 }
157 
158 /**
159  * chamelium_port_get_type:
160  * @port: The chamelium port to retrieve the type from
161  *
162  * Retrieves the DRM connector type of the physical port on the Chamelium. It
163  * should be noted that this type may differ from the type provided by the
164  * driver.
165  *
166  * Returns: the DRM connector type of the physical Chamelium port
167  */
chamelium_port_get_type(const struct chamelium_port * port)168 unsigned int chamelium_port_get_type(const struct chamelium_port *port) {
169 	return port->type;
170 }
171 
172 /**
173  * chamelium_port_get_connector:
174  * @chamelium: The Chamelium instance to use
175  * @port: The chamelium port to retrieve the DRM connector for
176  * @reprobe: Whether or not to reprobe the DRM connector
177  *
178  * Get a drmModeConnector object for the given Chamelium port, and optionally
179  * reprobe the port in the process
180  *
181  * Returns: a drmModeConnector object corresponding to the given port
182  */
chamelium_port_get_connector(struct chamelium * chamelium,struct chamelium_port * port,bool reprobe)183 drmModeConnector *chamelium_port_get_connector(struct chamelium *chamelium,
184 					       struct chamelium_port *port,
185 					       bool reprobe)
186 {
187 	drmModeConnector *connector;
188 
189 	if (reprobe)
190 		connector = drmModeGetConnector(chamelium->drm_fd,
191 						port->connector_id);
192 	else
193 		connector = drmModeGetConnectorCurrent(
194 		    chamelium->drm_fd, port->connector_id);
195 
196 	return connector;
197 }
198 
199 /**
200  * chamelium_port_get_name:
201  * @port: The chamelium port to retrieve the name of
202  *
203  * Gets the name of the DRM connector corresponding to the given Chamelium
204  * port.
205  *
206  * Returns: the name of the DRM connector
207  */
chamelium_port_get_name(struct chamelium_port * port)208 const char *chamelium_port_get_name(struct chamelium_port *port)
209 {
210 	return port->name;
211 }
212 
213 /**
214  * chamelium_destroy_frame_dump:
215  * @dump: The frame dump to destroy
216  *
217  * Destroys the given frame dump and frees all of the resources associated with
218  * it.
219  */
chamelium_destroy_frame_dump(struct chamelium_frame_dump * dump)220 void chamelium_destroy_frame_dump(struct chamelium_frame_dump *dump)
221 {
222 	free(dump->bgr);
223 	free(dump);
224 }
225 
chamelium_destroy_audio_file(struct chamelium_audio_file * audio_file)226 void chamelium_destroy_audio_file(struct chamelium_audio_file *audio_file)
227 {
228 	free(audio_file->path);
229 	free(audio_file);
230 }
231 
232 struct fsm_monitor_args {
233 	struct chamelium *chamelium;
234 	struct chamelium_port *port;
235 	struct udev_monitor *mon;
236 };
237 
238 /*
239  * Whenever resolutions or other factors change with the display output, the
240  * Chamelium's display receivers need to be fully reset in order to perform any
241  * frame-capturing related tasks. This requires cutting off the display then
242  * turning it back on, and is indicated by the Chamelium sending hotplug events
243  */
chamelium_fsm_mon(void * data)244 static void *chamelium_fsm_mon(void *data)
245 {
246 	struct fsm_monitor_args *args = data;
247 	drmModeConnector *connector;
248 	int drm_fd = args->chamelium->drm_fd;
249 
250 	/*
251 	 * Wait for the chamelium to try unplugging the connector, otherwise
252 	 * the thread calling chamelium_rpc will kill us
253 	 */
254 	igt_hotplug_detected(args->mon, 60);
255 
256 	/*
257 	 * Just in case the RPC call being executed returns before we complete
258 	 * the FSM modesetting sequence, so we don't leave the display in a bad
259 	 * state.
260 	 */
261 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
262 
263 	igt_debug("Chamelium needs FSM, handling\n");
264 	connector = chamelium_port_get_connector(args->chamelium, args->port,
265 						 false);
266 	kmstest_set_connector_dpms(drm_fd, connector, DRM_MODE_DPMS_OFF);
267 	kmstest_set_connector_dpms(drm_fd, connector, DRM_MODE_DPMS_ON);
268 
269 	drmModeFreeConnector(connector);
270 	return NULL;
271 }
272 
__chamelium_rpc_va(struct chamelium * chamelium,struct chamelium_port * fsm_port,const char * method_name,const char * format_str,va_list va_args)273 static xmlrpc_value *__chamelium_rpc_va(struct chamelium *chamelium,
274 					struct chamelium_port *fsm_port,
275 					const char *method_name,
276 					const char *format_str,
277 					va_list va_args)
278 {
279 	xmlrpc_value *res = NULL;
280 	struct fsm_monitor_args monitor_args;
281 	pthread_t fsm_thread_id;
282 
283 	/* Cleanup the last error, if any */
284 	if (chamelium->env.fault_occurred) {
285 		xmlrpc_env_clean(&chamelium->env);
286 		xmlrpc_env_init(&chamelium->env);
287 	}
288 
289 	/* Unfortunately xmlrpc_client's event loop helpers are rather useless
290 	 * for implementing any sort of event loop, since they provide no way
291 	 * to poll for events other then the RPC response. This means in order
292 	 * to handle the chamelium attempting FSM, we have to fork into another
293 	 * thread and have that handle hotplugging displays
294 	 */
295 	if (fsm_port) {
296 		monitor_args.chamelium = chamelium;
297 		monitor_args.port = fsm_port;
298 		monitor_args.mon = igt_watch_hotplug();
299 		pthread_create(&fsm_thread_id, NULL, chamelium_fsm_mon,
300 			       &monitor_args);
301 	}
302 
303 	xmlrpc_client_call2f_va(&chamelium->env, chamelium->client,
304 				chamelium->url, method_name, format_str, &res,
305 				va_args);
306 
307 	if (fsm_port) {
308 		pthread_cancel(fsm_thread_id);
309 		pthread_join(fsm_thread_id, NULL);
310 		igt_cleanup_hotplug(monitor_args.mon);
311 	}
312 
313 	return res;
314 }
315 
__chamelium_rpc(struct chamelium * chamelium,struct chamelium_port * fsm_port,const char * method_name,const char * format_str,...)316 static xmlrpc_value *__chamelium_rpc(struct chamelium *chamelium,
317 				     struct chamelium_port *fsm_port,
318 				     const char *method_name,
319 				     const char *format_str,
320 				     ...)
321 {
322 	xmlrpc_value *res;
323 	va_list va_args;
324 
325 	va_start(va_args, format_str);
326 	res = __chamelium_rpc_va(chamelium, fsm_port, method_name,
327 				 format_str, va_args);
328 	va_end(va_args);
329 
330 	return res;
331 }
332 
chamelium_rpc(struct chamelium * chamelium,struct chamelium_port * fsm_port,const char * method_name,const char * format_str,...)333 static xmlrpc_value *chamelium_rpc(struct chamelium *chamelium,
334 				   struct chamelium_port *fsm_port,
335 				   const char *method_name,
336 				   const char *format_str,
337 				   ...)
338 {
339 	xmlrpc_value *res;
340 	va_list va_args;
341 
342 	va_start(va_args, format_str);
343 	res = __chamelium_rpc_va(chamelium, fsm_port, method_name,
344 				 format_str, va_args);
345 	va_end(va_args);
346 
347 	igt_assert_f(!chamelium->env.fault_occurred,
348 		     "Chamelium RPC call failed: %s\n",
349 		     chamelium->env.fault_string);
350 
351 	return res;
352 }
353 
__chamelium_is_reachable(struct chamelium * chamelium)354 static bool __chamelium_is_reachable(struct chamelium *chamelium)
355 {
356 	xmlrpc_value *res;
357 
358 	/* GetSupportedInputs does not require a port and is harmless */
359 	res = __chamelium_rpc(chamelium, NULL, "GetSupportedInputs", "()");
360 
361 	if (res != NULL)
362 		xmlrpc_DECREF(res);
363 
364 	if (chamelium->env.fault_occurred)
365 		igt_debug("Chamelium RPC call failed: %s\n",
366 			  chamelium->env.fault_string);
367 
368 	return !chamelium->env.fault_occurred;
369 }
370 
chamelium_wait_reachable(struct chamelium * chamelium,int timeout)371 void chamelium_wait_reachable(struct chamelium *chamelium, int timeout)
372 {
373 	bool chamelium_online = igt_wait(__chamelium_is_reachable(chamelium),
374 					 timeout * 1000, 100);
375 
376 	igt_assert_f(chamelium_online,
377 		     "Couldn't connect to Chamelium for %ds", timeout);
378 }
379 
380 /**
381  * chamelium_plug:
382  * @chamelium: The Chamelium instance to use
383  * @port: The port on the chamelium to plug
384  *
385  * Simulate a display connector being plugged into the system using the
386  * chamelium.
387  */
chamelium_plug(struct chamelium * chamelium,struct chamelium_port * port)388 void chamelium_plug(struct chamelium *chamelium, struct chamelium_port *port)
389 {
390 	igt_debug("Plugging %s (Chamelium port ID %d)\n", port->name, port->id);
391 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Plug", "(i)", port->id));
392 }
393 
394 /**
395  * chamelium_unplug:
396  * @chamelium: The Chamelium instance to use
397  * @port: The port on the chamelium to unplug
398  *
399  * Simulate a display connector being unplugged from the system using the
400  * chamelium.
401  */
chamelium_unplug(struct chamelium * chamelium,struct chamelium_port * port)402 void chamelium_unplug(struct chamelium *chamelium, struct chamelium_port *port)
403 {
404 	igt_debug("Unplugging port %s\n", port->name);
405 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Unplug", "(i)",
406 				    port->id));
407 }
408 
409 /**
410  * chamelium_is_plugged:
411  * @chamelium: The Chamelium instance to use
412  * @port: The port on the Chamelium to check the status of
413  *
414  * Check whether or not the given port has been plugged into the system using
415  * #chamelium_plug.
416  *
417  * Returns: %true if the connector is set to plugged in, %false otherwise.
418  */
chamelium_is_plugged(struct chamelium * chamelium,struct chamelium_port * port)419 bool chamelium_is_plugged(struct chamelium *chamelium,
420 			  struct chamelium_port *port)
421 {
422 	xmlrpc_value *res;
423 	xmlrpc_bool is_plugged;
424 
425 	res = chamelium_rpc(chamelium, NULL, "IsPlugged", "(i)", port->id);
426 
427 	xmlrpc_read_bool(&chamelium->env, res, &is_plugged);
428 	xmlrpc_DECREF(res);
429 
430 	return is_plugged;
431 }
432 
433 /**
434  * chamelium_port_wait_video_input_stable:
435  * @chamelium: The Chamelium instance to use
436  * @port: The port on the Chamelium to check the status of
437  * @timeout_secs: How long to wait for a video signal to appear before timing
438  * out
439  *
440  * Waits for a video signal to appear on the given port. This is useful for
441  * checking whether or not we've setup a monitor correctly.
442  *
443  * Returns: %true if a video signal was detected, %false if we timed out
444  */
chamelium_port_wait_video_input_stable(struct chamelium * chamelium,struct chamelium_port * port,int timeout_secs)445 bool chamelium_port_wait_video_input_stable(struct chamelium *chamelium,
446 					    struct chamelium_port *port,
447 					    int timeout_secs)
448 {
449 	xmlrpc_value *res;
450 	xmlrpc_bool is_on;
451 
452 	igt_debug("Waiting for video input to stabalize on %s\n", port->name);
453 
454 	res = chamelium_rpc(chamelium, port, "WaitVideoInputStable", "(ii)",
455 			    port->id, timeout_secs);
456 
457 	xmlrpc_read_bool(&chamelium->env, res, &is_on);
458 	xmlrpc_DECREF(res);
459 
460 	return is_on;
461 }
462 
463 /**
464  * chamelium_fire_hpd_pulses:
465  * @chamelium: The Chamelium instance to use
466  * @port: The port to fire the HPD pulses on
467  * @width_msec: How long each pulse should last
468  * @count: The number of pulses to send
469  *
470  * A convienence function for sending multiple hotplug pulses to the system.
471  * The pulses start at low (e.g. connector is disconnected), and then alternate
472  * from high (e.g. connector is plugged in) to low. This is the equivalent of
473  * repeatedly calling #chamelium_plug and #chamelium_unplug, waiting
474  * @width_msec between each call.
475  *
476  * If @count is even, the last pulse sent will be high, and if it's odd then it
477  * will be low. Resetting the HPD line back to it's previous state, if desired,
478  * is the responsibility of the caller.
479  */
chamelium_fire_hpd_pulses(struct chamelium * chamelium,struct chamelium_port * port,int width_msec,int count)480 void chamelium_fire_hpd_pulses(struct chamelium *chamelium,
481 			       struct chamelium_port *port,
482 			       int width_msec, int count)
483 {
484 	xmlrpc_value *pulse_widths = xmlrpc_array_new(&chamelium->env);
485 	xmlrpc_value *width = xmlrpc_int_new(&chamelium->env, width_msec);
486 	int i;
487 
488 	igt_debug("Firing %d HPD pulses with width of %d msec on %s\n",
489 		  count, width_msec, port->name);
490 
491 	for (i = 0; i < count; i++)
492 		xmlrpc_array_append_item(&chamelium->env, pulse_widths, width);
493 
494 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "FireMixedHpdPulses",
495 				    "(iA)", port->id, pulse_widths));
496 
497 	xmlrpc_DECREF(width);
498 	xmlrpc_DECREF(pulse_widths);
499 }
500 
501 /**
502  * chamelium_fire_mixed_hpd_pulses:
503  * @chamelium: The Chamelium instance to use
504  * @port: The port to fire the HPD pulses on
505  * @...: The length of each pulse in milliseconds, terminated with a %0
506  *
507  * Does the same thing as #chamelium_fire_hpd_pulses, but allows the caller to
508  * specify the length of each individual pulse.
509  */
chamelium_fire_mixed_hpd_pulses(struct chamelium * chamelium,struct chamelium_port * port,...)510 void chamelium_fire_mixed_hpd_pulses(struct chamelium *chamelium,
511 				     struct chamelium_port *port, ...)
512 {
513 	va_list args;
514 	xmlrpc_value *pulse_widths = xmlrpc_array_new(&chamelium->env), *width;
515 	int arg;
516 
517 	igt_debug("Firing mixed HPD pulses on %s\n", port->name);
518 
519 	va_start(args, port);
520 	for (arg = va_arg(args, int); arg; arg = va_arg(args, int)) {
521 		width = xmlrpc_int_new(&chamelium->env, arg);
522 		xmlrpc_array_append_item(&chamelium->env, pulse_widths, width);
523 		xmlrpc_DECREF(width);
524 	}
525 	va_end(args);
526 
527 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "FireMixedHpdPulses",
528 				    "(iA)", port->id, pulse_widths));
529 
530 	xmlrpc_DECREF(pulse_widths);
531 }
532 
533 /**
534  * chamelium_schedule_hpd_toggle:
535  * @chamelium: The Chamelium instance to use
536  * @port: The port to fire the HPD pulses on
537  * @delay_ms: Delay in milli-second before the toggle takes place
538  * @rising_edge: Whether the toggle should be a rising edge or a falling edge
539  *
540  * Instructs the chamelium to schedule an hpd toggle (either a rising edge or
541  * a falling edge, depending on @rising_edg) after @delay_ms have passed.
542  * This is useful for testing things such as hpd after a suspend/resume cycle.
543  */
chamelium_schedule_hpd_toggle(struct chamelium * chamelium,struct chamelium_port * port,int delay_ms,bool rising_edge)544 void chamelium_schedule_hpd_toggle(struct chamelium *chamelium,
545 				   struct chamelium_port *port, int delay_ms,
546 				   bool rising_edge)
547 {
548 	igt_debug("Scheduling HPD toggle on %s in %d ms\n", port->name,
549 		  delay_ms);
550 
551 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "ScheduleHpdToggle",
552 				    "(iii)", port->id, delay_ms, rising_edge));
553 }
554 
chamelium_upload_edid(struct chamelium * chamelium,const struct edid * edid)555 static int chamelium_upload_edid(struct chamelium *chamelium,
556 				 const struct edid *edid)
557 {
558 	xmlrpc_value *res;
559 	int edid_id;
560 
561 	res = chamelium_rpc(chamelium, NULL, "CreateEdid", "(6)",
562 			    edid, edid_get_size(edid));
563 	xmlrpc_read_int(&chamelium->env, res, &edid_id);
564 	xmlrpc_DECREF(res);
565 
566 	return edid_id;
567 }
568 
chamelium_destroy_edid(struct chamelium * chamelium,int edid_id)569 static void chamelium_destroy_edid(struct chamelium *chamelium, int edid_id)
570 {
571 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "DestroyEdid", "(i)",
572 				    edid_id));
573 }
574 
575 /**
576  * chamelium_new_edid:
577  * @chamelium: The Chamelium instance to use
578  * @edid: The edid blob to upload to the chamelium
579  *
580  * Uploads and registers a new EDID with the chamelium. The EDID will be
581  * destroyed automatically when #chamelium_deinit is called.
582  *
583  * Callers shouldn't assume that the raw EDID they provide is uploaded as-is to
584  * the Chamelium. The EDID may be mutated (e.g. a serial number can be appended
585  * to be able to uniquely identify the EDID). To retrieve the exact EDID that
586  * will be applied to a particular port, use #chamelium_edid_get_raw.
587  *
588  * Returns: An opaque pointer to the Chamelium EDID
589  */
chamelium_new_edid(struct chamelium * chamelium,const struct edid * edid)590 struct chamelium_edid *chamelium_new_edid(struct chamelium *chamelium,
591 					  const struct edid *edid)
592 {
593 	struct chamelium_edid *chamelium_edid;
594 	size_t edid_size = edid_get_size(edid);
595 
596 	chamelium_edid = calloc(1, sizeof(struct chamelium_edid));
597 	chamelium_edid->chamelium = chamelium;
598 	chamelium_edid->base = malloc(edid_size);
599 	memcpy(chamelium_edid->base, edid, edid_size);
600 	igt_list_add(&chamelium_edid->link, &chamelium->edids);
601 
602 	return chamelium_edid;
603 }
604 
605 /**
606  * chamelium_port_tag_edid: tag the EDID with the provided Chamelium port.
607  */
chamelium_port_tag_edid(struct chamelium_port * port,struct edid * edid)608 static void chamelium_port_tag_edid(struct chamelium_port *port,
609 				    struct edid *edid)
610 {
611 	uint32_t *serial;
612 
613 	/* Product code: Chamelium */
614 	edid->prod_code[0] = 'C';
615 	edid->prod_code[1] = 'H';
616 
617 	/* Serial: Chamelium port ID */
618 	serial = (uint32_t *) &edid->serial;
619 	*serial = port->id;
620 
621 	edid_update_checksum(edid);
622 }
623 
624 /**
625  * chamelium_edid_get_raw: get the raw EDID
626  * @edid: the Chamelium EDID
627  * @port: the Chamelium port
628  *
629  * The EDID provided to #chamelium_new_edid may be mutated for identification
630  * purposes. This function allows to retrieve the exact EDID that will be set
631  * for a given port.
632  *
633  * The returned raw EDID is only valid until the next call to this function.
634  */
chamelium_edid_get_raw(struct chamelium_edid * edid,struct chamelium_port * port)635 const struct edid *chamelium_edid_get_raw(struct chamelium_edid *edid,
636 					  struct chamelium_port *port)
637 {
638 	size_t port_index = port - edid->chamelium->ports;
639 	size_t edid_size;
640 
641 	if (!edid->raw[port_index]) {
642 		edid_size = edid_get_size(edid->base);
643 		edid->raw[port_index] = malloc(edid_size);
644 		memcpy(edid->raw[port_index], edid->base, edid_size);
645 		chamelium_port_tag_edid(port, edid->raw[port_index]);
646 	}
647 
648 	return edid->raw[port_index];
649 }
650 
651 /**
652  * chamelium_port_set_edid:
653  * @chamelium: The Chamelium instance to use
654  * @port: The port on the Chamelium to set the EDID on
655  * @edid: The Chamelium EDID to set or NULL to use the default Chamelium EDID
656  *
657  * Sets a port on the chamelium to use the specified EDID. This does not fire a
658  * hotplug pulse on it's own, and merely changes what EDID the chamelium port
659  * will report to us the next time we probe it. Users will need to reprobe the
660  * connectors themselves if they want to see the EDID reported by the port
661  * change.
662  *
663  * To create an EDID, see #chamelium_new_edid.
664  */
chamelium_port_set_edid(struct chamelium * chamelium,struct chamelium_port * port,struct chamelium_edid * edid)665 void chamelium_port_set_edid(struct chamelium *chamelium,
666 			     struct chamelium_port *port,
667 			     struct chamelium_edid *edid)
668 {
669 	int edid_id;
670 	size_t port_index;
671 	const struct edid *raw_edid;
672 
673 	if (edid) {
674 		port_index = port - chamelium->ports;
675 		edid_id = edid->ids[port_index];
676 		if (edid_id == 0) {
677 			raw_edid = chamelium_edid_get_raw(edid, port);
678 			edid_id = chamelium_upload_edid(chamelium, raw_edid);
679 			edid->ids[port_index] = edid_id;
680 		}
681 	} else {
682 		edid_id = 0;
683 	}
684 
685 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "ApplyEdid", "(ii)",
686 				    port->id, edid_id));
687 }
688 
689 /**
690  * chamelium_port_set_ddc_state:
691  * @chamelium: The Chamelium instance to use
692  * @port: The port to change the DDC state on
693  * @enabled: Whether or not to enable the DDC bus
694  *
695  * This disables the DDC bus (e.g. the i2c line on the connector that gives us
696  * an EDID) of the specified port on the chamelium. This is useful for testing
697  * behavior on legacy connectors such as VGA, where the presence of a DDC bus
698  * is not always guaranteed.
699  */
chamelium_port_set_ddc_state(struct chamelium * chamelium,struct chamelium_port * port,bool enabled)700 void chamelium_port_set_ddc_state(struct chamelium *chamelium,
701 				  struct chamelium_port *port,
702 				  bool enabled)
703 {
704 	igt_debug("%sabling DDC bus on %s\n",
705 		  enabled ? "En" : "Dis", port->name);
706 
707 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "SetDdcState", "(ib)",
708 				    port->id, enabled));
709 }
710 
711 /**
712  * chamelium_port_get_ddc_state:
713  * @chamelium: The Chamelium instance to use
714  * @port: The port on the Chamelium to check the status of
715  *
716  * Check whether or not the DDC bus on the specified chamelium port is enabled
717  * or not.
718  *
719  * Returns: %true if the DDC bus is enabled, %false otherwise.
720  */
chamelium_port_get_ddc_state(struct chamelium * chamelium,struct chamelium_port * port)721 bool chamelium_port_get_ddc_state(struct chamelium *chamelium,
722 				  struct chamelium_port *port)
723 {
724 	xmlrpc_value *res;
725 	xmlrpc_bool enabled;
726 
727 	res = chamelium_rpc(chamelium, NULL, "IsDdcEnabled", "(i)", port->id);
728 	xmlrpc_read_bool(&chamelium->env, res, &enabled);
729 
730 	xmlrpc_DECREF(res);
731 	return enabled;
732 }
733 
734 /**
735  * chamelium_port_get_resolution:
736  * @chamelium: The Chamelium instance to use
737  * @port: The port on the Chamelium to check
738  * @x: Where to store the horizontal resolution of the port
739  * @y: Where to store the verical resolution of the port
740  *
741  * Check the current reported display resolution of the specified port on the
742  * chamelium. This information is provided by the chamelium itself, not DRM.
743  * Useful for verifying that we really are scanning out at the resolution we
744  * think we are.
745  */
chamelium_port_get_resolution(struct chamelium * chamelium,struct chamelium_port * port,int * x,int * y)746 void chamelium_port_get_resolution(struct chamelium *chamelium,
747 				   struct chamelium_port *port,
748 				   int *x, int *y)
749 {
750 	xmlrpc_value *res, *res_x, *res_y;
751 
752 	res = chamelium_rpc(chamelium, port, "DetectResolution", "(i)",
753 			    port->id);
754 
755 	xmlrpc_array_read_item(&chamelium->env, res, 0, &res_x);
756 	xmlrpc_array_read_item(&chamelium->env, res, 1, &res_y);
757 	xmlrpc_read_int(&chamelium->env, res_x, x);
758 	xmlrpc_read_int(&chamelium->env, res_y, y);
759 
760 	xmlrpc_DECREF(res_x);
761 	xmlrpc_DECREF(res_y);
762 	xmlrpc_DECREF(res);
763 }
764 
765 /** chamelium_supports_method: checks if the Chamelium board supports a method.
766  *
767  * Note: this actually tries to call the method.
768  *
769  * See https://crbug.com/977995 for a discussion about a better solution.
770  */
chamelium_supports_method(struct chamelium * chamelium,const char * name)771 static bool chamelium_supports_method(struct chamelium *chamelium,
772 				      const char *name)
773 {
774 	xmlrpc_value *res;
775 
776 	res = __chamelium_rpc(chamelium, NULL, name, "()");
777 	if (res)
778 		xmlrpc_DECREF(res);
779 
780 	/* XML-RPC has a special code for unsupported methods
781 	 * (XMLRPC_NO_SUCH_METHOD_ERROR) however the Chamelium implementation
782 	 * doesn't return it. */
783 	return (!chamelium->env.fault_occurred ||
784 		strstr(chamelium->env.fault_string, "not supported") == NULL);
785 }
786 
chamelium_supports_get_video_params(struct chamelium * chamelium)787 bool chamelium_supports_get_video_params(struct chamelium *chamelium)
788 {
789 	return chamelium_supports_method(chamelium, "GetVideoParams");
790 }
791 
read_int_from_xml_struct(struct chamelium * chamelium,xmlrpc_value * struct_val,const char * key,int * dst)792 static void read_int_from_xml_struct(struct chamelium *chamelium,
793 				     xmlrpc_value *struct_val, const char *key,
794 				     int *dst)
795 {
796 	xmlrpc_value *val = NULL;
797 
798 	xmlrpc_struct_find_value(&chamelium->env, struct_val, key, &val);
799 	if (val) {
800 		xmlrpc_read_int(&chamelium->env, val, dst);
801 		xmlrpc_DECREF(val);
802 	} else
803 		*dst = -1;
804 }
805 
video_params_from_xml(struct chamelium * chamelium,xmlrpc_value * res,struct chamelium_video_params * params)806 static void video_params_from_xml(struct chamelium *chamelium,
807 				  xmlrpc_value *res,
808 				  struct chamelium_video_params *params)
809 {
810 	xmlrpc_value *val = NULL;
811 
812 	xmlrpc_struct_find_value(&chamelium->env, res, "clock", &val);
813 	if (val) {
814 		xmlrpc_read_double(&chamelium->env, val, &params->clock);
815 		xmlrpc_DECREF(val);
816 	} else
817 		params->clock = NAN;
818 
819 	read_int_from_xml_struct(chamelium, res, "htotal", &params->htotal);
820 	read_int_from_xml_struct(chamelium, res, "hactive", &params->hactive);
821 	read_int_from_xml_struct(chamelium, res, "hsync_offset",
822 				 &params->hsync_offset);
823 	read_int_from_xml_struct(chamelium, res, "hsync_width",
824 				 &params->hsync_width);
825 	read_int_from_xml_struct(chamelium, res, "hsync_polarity",
826 				 &params->hsync_polarity);
827 	read_int_from_xml_struct(chamelium, res, "vtotal", &params->vtotal);
828 	read_int_from_xml_struct(chamelium, res, "vactive", &params->vactive);
829 	read_int_from_xml_struct(chamelium, res, "vsync_offset",
830 				 &params->vsync_offset);
831 	read_int_from_xml_struct(chamelium, res, "vsync_width",
832 				 &params->vsync_width);
833 	read_int_from_xml_struct(chamelium, res, "vsync_polarity",
834 				 &params->vsync_polarity);
835 }
836 
chamelium_port_get_video_params(struct chamelium * chamelium,struct chamelium_port * port,struct chamelium_video_params * params)837 void chamelium_port_get_video_params(struct chamelium *chamelium,
838 				     struct chamelium_port *port,
839 				     struct chamelium_video_params *params)
840 {
841 	xmlrpc_value *res;
842 
843 	res = chamelium_rpc(chamelium, NULL, "GetVideoParams", "(i)", port->id);
844 	video_params_from_xml(chamelium, res, params);
845 
846 	xmlrpc_DECREF(res);
847 }
848 
chamelium_get_captured_resolution(struct chamelium * chamelium,int * w,int * h)849 static void chamelium_get_captured_resolution(struct chamelium *chamelium,
850 					      int *w, int *h)
851 {
852 	xmlrpc_value *res, *res_w, *res_h;
853 
854 	res = chamelium_rpc(chamelium, NULL, "GetCapturedResolution", "()");
855 
856 	xmlrpc_array_read_item(&chamelium->env, res, 0, &res_w);
857 	xmlrpc_array_read_item(&chamelium->env, res, 1, &res_h);
858 	xmlrpc_read_int(&chamelium->env, res_w, w);
859 	xmlrpc_read_int(&chamelium->env, res_h, h);
860 
861 	xmlrpc_DECREF(res_w);
862 	xmlrpc_DECREF(res_h);
863 	xmlrpc_DECREF(res);
864 }
865 
frame_from_xml(struct chamelium * chamelium,xmlrpc_value * frame_xml)866 static struct chamelium_frame_dump *frame_from_xml(struct chamelium *chamelium,
867 						   xmlrpc_value *frame_xml)
868 {
869 	struct chamelium_frame_dump *ret = malloc(sizeof(*ret));
870 
871 	chamelium_get_captured_resolution(chamelium, &ret->width, &ret->height);
872 	ret->port = chamelium->capturing_port;
873 	xmlrpc_read_base64(&chamelium->env, frame_xml, &ret->size,
874 			   (void*)&ret->bgr);
875 
876 	return ret;
877 }
878 
879 /**
880  * chamelium_port_dump_pixels:
881  * @chamelium: The Chamelium instance to use
882  * @port: The port to perform the video capture on
883  * @x: The X coordinate to crop the screen capture to
884  * @y: The Y coordinate to crop the screen capture to
885  * @w: The width of the area to crop the screen capture to, or 0 for the whole
886  * screen
887  * @h: The height of the area to crop the screen capture to, or 0 for the whole
888  * screen
889  *
890  * Captures the currently displayed image on the given chamelium port,
891  * optionally cropped to a given region. In situations where pre-calculating
892  * CRCs may not be reliable, this can be used as an alternative for figuring
893  * out whether or not the correct images are being displayed on the screen.
894  *
895  * The frame dump data returned by this function should be freed when the
896  * caller is done with it using #chamelium_destroy_frame_dump.
897  *
898  * As an important note: some of the EDIDs provided by the Chamelium cause
899  * certain GPU drivers to default to using limited color ranges. This can cause
900  * video captures from the Chamelium to provide different images then expected
901  * due to the difference in color ranges (framebuffer uses full color range,
902  * but the video output doesn't), and as a result lead to CRC mismatches. To
903  * workaround this, the caller should force the connector to use full color
904  * ranges by using #kmstest_set_connector_broadcast_rgb before setting up the
905  * display.
906  *
907  * Returns: a chamelium_frame_dump struct
908  */
chamelium_port_dump_pixels(struct chamelium * chamelium,struct chamelium_port * port,int x,int y,int w,int h)909 struct chamelium_frame_dump *chamelium_port_dump_pixels(struct chamelium *chamelium,
910 							struct chamelium_port *port,
911 							int x, int y,
912 							int w, int h)
913 {
914 	xmlrpc_value *res;
915 	struct chamelium_frame_dump *frame;
916 
917 	res = chamelium_rpc(chamelium, port, "DumpPixels",
918 			    (w && h) ? "(iiiii)" : "(innnn)",
919 			    port->id, x, y, w, h);
920 	chamelium->capturing_port = port;
921 
922 	frame = frame_from_xml(chamelium, res);
923 	xmlrpc_DECREF(res);
924 
925 	return frame;
926 }
927 
crc_from_xml(struct chamelium * chamelium,xmlrpc_value * xml_crc,igt_crc_t * out)928 static void crc_from_xml(struct chamelium *chamelium,
929 			 xmlrpc_value *xml_crc, igt_crc_t *out)
930 {
931 	xmlrpc_value *res;
932 	int i;
933 
934 	out->n_words = xmlrpc_array_size(&chamelium->env, xml_crc);
935 	for (i = 0; i < out->n_words; i++) {
936 		xmlrpc_array_read_item(&chamelium->env, xml_crc, i, &res);
937 		xmlrpc_read_int(&chamelium->env, res, (int*)&out->crc[i]);
938 		xmlrpc_DECREF(res);
939 	}
940 }
941 
942 /**
943  * chamelium_get_crc_for_area:
944  * @chamelium: The Chamelium instance to use
945  * @port: The port to perform the CRC checking on
946  * @x: The X coordinate on the emulated display to start calculating the CRC
947  * from
948  * @y: The Y coordinate on the emulated display to start calculating the CRC
949  * from
950  * @w: The width of the area to fetch the CRC from, or %0 for the whole display
951  * @h: The height of the area to fetch the CRC from, or %0 for the whole display
952  *
953  * Reads back the pixel CRC for an area on the specified chamelium port. This
954  * is the same as using the CRC readback from a GPU, the main difference being
955  * the data is provided by the chamelium and also allows us to specify a region
956  * of the screen to use as opposed to the entire thing.
957  *
958  * As an important note: some of the EDIDs provided by the Chamelium cause
959  * certain GPU drivers to default to using limited color ranges. This can cause
960  * video captures from the Chamelium to provide different images then expected
961  * due to the difference in color ranges (framebuffer uses full color range,
962  * but the video output doesn't), and as a result lead to CRC mismatches. To
963  * workaround this, the caller should force the connector to use full color
964  * ranges by using #kmstest_set_connector_broadcast_rgb before setting up the
965  * display.
966  *
967  * After the caller is finished with the EDID returned by this function, the
968  * caller should manually free the resources associated with it.
969  *
970  * Returns: The CRC read back from the chamelium
971  */
chamelium_get_crc_for_area(struct chamelium * chamelium,struct chamelium_port * port,int x,int y,int w,int h)972 igt_crc_t *chamelium_get_crc_for_area(struct chamelium *chamelium,
973 				      struct chamelium_port *port,
974 				      int x, int y, int w, int h)
975 {
976 	xmlrpc_value *res;
977 	igt_crc_t *ret = malloc(sizeof(igt_crc_t));
978 
979 	res = chamelium_rpc(chamelium, port, "ComputePixelChecksum",
980 			    (w && h) ? "(iiiii)" : "(innnn)",
981 			    port->id, x, y, w, h);
982 	chamelium->capturing_port = port;
983 
984 	crc_from_xml(chamelium, res, ret);
985 	xmlrpc_DECREF(res);
986 
987 	return ret;
988 }
989 
990 /**
991  * chamelium_start_capture:
992  * @chamelium: The Chamelium instance to use
993  * @port: The port to perform the video capture on
994  * @x: The X coordinate to crop the video to
995  * @y: The Y coordinate to crop the video to
996  * @w: The width of the cropped video, or %0 for the whole display
997  * @h: The height of the cropped video, or %0 for the whole display
998  *
999  * Starts capturing video frames on the given Chamelium port. Once the user is
1000  * finished capturing frames, they should call #chamelium_stop_capture.
1001  *
1002  * A blocking, one-shot version of this function is available: see
1003  * #chamelium_capture
1004  *
1005  * As an important note: some of the EDIDs provided by the Chamelium cause
1006  * certain GPU drivers to default to using limited color ranges. This can cause
1007  * video captures from the Chamelium to provide different images then expected
1008  * due to the difference in color ranges (framebuffer uses full color range,
1009  * but the video output doesn't), and as a result lead to CRC and frame dump
1010  * comparison mismatches. To workaround this, the caller should force the
1011  * connector to use full color ranges by using
1012  * #kmstest_set_connector_broadcast_rgb before setting up the display.
1013  */
chamelium_start_capture(struct chamelium * chamelium,struct chamelium_port * port,int x,int y,int w,int h)1014 void chamelium_start_capture(struct chamelium *chamelium,
1015 			     struct chamelium_port *port, int x, int y, int w, int h)
1016 {
1017 	xmlrpc_DECREF(chamelium_rpc(chamelium, port, "StartCapturingVideo",
1018 				    (w && h) ? "(iiiii)" : "(innnn)",
1019 				    port->id, x, y, w, h));
1020 	chamelium->capturing_port = port;
1021 }
1022 
1023 /**
1024  * chamelium_stop_capture:
1025  * @chamelium: The Chamelium instance to use
1026  * @frame_count: The number of frames to wait to capture, or %0 to stop
1027  * immediately
1028  *
1029  * Finishes capturing video frames on the given Chamelium port. If @frame_count
1030  * is specified, this call will block until the given number of frames have been
1031  * captured.
1032  */
chamelium_stop_capture(struct chamelium * chamelium,int frame_count)1033 void chamelium_stop_capture(struct chamelium *chamelium, int frame_count)
1034 {
1035 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "StopCapturingVideo",
1036 				    "(i)", frame_count));
1037 }
1038 
1039 /**
1040  * chamelium_capture:
1041  * @chamelium: The Chamelium instance to use
1042  * @port: The port to perform the video capture on
1043  * @x: The X coordinate to crop the video to
1044  * @y: The Y coordinate to crop the video to
1045  * @w: The width of the cropped video, or %0 for the whole display
1046  * @h: The height of the cropped video, or %0 for the whole display
1047  * @frame_count: The number of frames to capture
1048  *
1049  * Captures the given number of frames on the chamelium. This is equivalent to
1050  * calling #chamelium_start_capture immediately followed by
1051  * #chamelium_stop_capture. The caller is blocked until all of the frames have
1052  * been captured.
1053  *
1054  * As an important note: some of the EDIDs provided by the Chamelium cause
1055  * certain GPU drivers to default to using limited color ranges. This can cause
1056  * video captures from the Chamelium to provide different images then expected
1057  * due to the difference in color ranges (framebuffer uses full color range,
1058  * but the video output doesn't), and as a result lead to CRC and frame dump
1059  * comparison mismatches. To workaround this, the caller should force the
1060  * connector to use full color ranges by using
1061  * #kmstest_set_connector_broadcast_rgb before setting up the display.
1062  */
chamelium_capture(struct chamelium * chamelium,struct chamelium_port * port,int x,int y,int w,int h,int frame_count)1063 void chamelium_capture(struct chamelium *chamelium, struct chamelium_port *port,
1064 		       int x, int y, int w, int h, int frame_count)
1065 {
1066 	xmlrpc_DECREF(chamelium_rpc(chamelium, port, "CaptureVideo",
1067 				    (w && h) ? "(iiiiii)" : "(iinnnn)",
1068 				    port->id, frame_count, x, y, w, h));
1069 	chamelium->capturing_port = port;
1070 }
1071 
1072 /**
1073  * chamelium_read_captured_crcs:
1074  * @chamelium: The Chamelium instance to use
1075  * @frame_count: Where to store the number of CRCs we read in
1076  *
1077  * Reads all of the CRCs that have been captured thus far from the Chamelium.
1078  *
1079  * Returns: An array of @frame_count length containing all of the CRCs we read
1080  */
chamelium_read_captured_crcs(struct chamelium * chamelium,int * frame_count)1081 igt_crc_t *chamelium_read_captured_crcs(struct chamelium *chamelium,
1082 					int *frame_count)
1083 {
1084 	igt_crc_t *ret;
1085 	xmlrpc_value *res, *elem;
1086 	int i;
1087 
1088 	res = chamelium_rpc(chamelium, NULL, "GetCapturedChecksums", "(in)", 0);
1089 
1090 	*frame_count = xmlrpc_array_size(&chamelium->env, res);
1091 	ret = calloc(sizeof(igt_crc_t), *frame_count);
1092 
1093 	for (i = 0; i < *frame_count; i++) {
1094 		xmlrpc_array_read_item(&chamelium->env, res, i, &elem);
1095 
1096 		crc_from_xml(chamelium, elem, &ret[i]);
1097 		ret[i].frame = i;
1098 
1099 		xmlrpc_DECREF(elem);
1100 	}
1101 
1102 	xmlrpc_DECREF(res);
1103 
1104 	return ret;
1105 }
1106 
1107 /**
1108  * chamelium_port_read_captured_frame:
1109  *
1110  * @chamelium: The Chamelium instance to use
1111  * @index: The index of the captured frame we want to get
1112  *
1113  * Retrieves a single video frame captured during the last video capture on the
1114  * Chamelium. This data should be freed using #chamelium_destroy_frame_data
1115  *
1116  * Returns: a chamelium_frame_dump struct
1117  */
chamelium_read_captured_frame(struct chamelium * chamelium,unsigned int index)1118 struct chamelium_frame_dump *chamelium_read_captured_frame(struct chamelium *chamelium,
1119 							   unsigned int index)
1120 {
1121 	xmlrpc_value *res;
1122 	struct chamelium_frame_dump *frame;
1123 
1124 	res = chamelium_rpc(chamelium, NULL, "ReadCapturedFrame", "(i)", index);
1125 	frame = frame_from_xml(chamelium, res);
1126 	xmlrpc_DECREF(res);
1127 
1128 	return frame;
1129 }
1130 
1131 /**
1132  * chamelium_get_captured_frame_count:
1133  * @chamelium: The Chamelium instance to use
1134  *
1135  * Gets the number of frames that were captured during the last video capture.
1136  *
1137  * Returns: the number of frames the Chamelium captured during the last video
1138  * capture.
1139  */
chamelium_get_captured_frame_count(struct chamelium * chamelium)1140 int chamelium_get_captured_frame_count(struct chamelium *chamelium)
1141 {
1142 	xmlrpc_value *res;
1143 	int ret;
1144 
1145 	res = chamelium_rpc(chamelium, NULL, "GetCapturedFrameCount", "()");
1146 	xmlrpc_read_int(&chamelium->env, res, &ret);
1147 
1148 	xmlrpc_DECREF(res);
1149 	return ret;
1150 }
1151 
chamelium_supports_get_last_infoframe(struct chamelium * chamelium)1152 bool chamelium_supports_get_last_infoframe(struct chamelium *chamelium)
1153 {
1154 	return chamelium_supports_method(chamelium, "GetLastInfoFrame");
1155 }
1156 
1157 static const char *
chamelium_infoframe_type_str(enum chamelium_infoframe_type type)1158 chamelium_infoframe_type_str(enum chamelium_infoframe_type type)
1159 {
1160 	switch (type) {
1161 	case CHAMELIUM_INFOFRAME_AVI:
1162 		return "avi";
1163 	case CHAMELIUM_INFOFRAME_AUDIO:
1164 		return "audio";
1165 	case CHAMELIUM_INFOFRAME_MPEG:
1166 		return "mpeg";
1167 	case CHAMELIUM_INFOFRAME_VENDOR:
1168 		return "vendor";
1169 	}
1170 	assert(0); /* unreachable */
1171 }
1172 
1173 struct chamelium_infoframe *
chamelium_get_last_infoframe(struct chamelium * chamelium,struct chamelium_port * port,enum chamelium_infoframe_type type)1174 chamelium_get_last_infoframe(struct chamelium *chamelium,
1175 			     struct chamelium_port *port,
1176 			     enum chamelium_infoframe_type type)
1177 {
1178 	xmlrpc_value *res, *res_version, *res_payload;
1179 	struct chamelium_infoframe *infoframe;
1180 	const unsigned char *payload;
1181 
1182 	res = chamelium_rpc(chamelium, NULL, "GetLastInfoFrame", "(is)",
1183 			    port->id, chamelium_infoframe_type_str(type));
1184 	xmlrpc_struct_find_value(&chamelium->env, res, "version", &res_version);
1185 	xmlrpc_struct_find_value(&chamelium->env, res, "payload", &res_payload);
1186 	infoframe = calloc(1, sizeof(*infoframe));
1187 	xmlrpc_read_int(&chamelium->env, res_version, &infoframe->version);
1188 	xmlrpc_read_base64(&chamelium->env, res_payload,
1189 			   &infoframe->payload_size, &payload);
1190 	/* xmlrpc-c's docs say payload is actually not constant */
1191 	infoframe->payload = (uint8_t *) payload;
1192 	xmlrpc_DECREF(res_version);
1193 	xmlrpc_DECREF(res_payload);
1194 	xmlrpc_DECREF(res);
1195 
1196 	if (infoframe->payload_size == 0) {
1197 		chamelium_infoframe_destroy(infoframe);
1198 		return NULL;
1199 	}
1200 	return infoframe;
1201 }
1202 
chamelium_infoframe_destroy(struct chamelium_infoframe * infoframe)1203 void chamelium_infoframe_destroy(struct chamelium_infoframe *infoframe)
1204 {
1205 	free(infoframe->payload);
1206 	free(infoframe);
1207 }
1208 
chamelium_supports_trigger_link_failure(struct chamelium * chamelium)1209 bool chamelium_supports_trigger_link_failure(struct chamelium *chamelium)
1210 {
1211 	return chamelium_supports_method(chamelium, "TriggerLinkFailure");
1212 }
1213 
1214 /**
1215  * chamelium_trigger_link_failure: trigger a link failure on the provided port.
1216  */
chamelium_trigger_link_failure(struct chamelium * chamelium,struct chamelium_port * port)1217 void chamelium_trigger_link_failure(struct chamelium *chamelium,
1218 				    struct chamelium_port *port)
1219 {
1220 	xmlrpc_DECREF(chamelium_rpc(chamelium, port, "TriggerLinkFailure",
1221 				    "(i)", port->id));
1222 }
1223 
chamelium_has_audio_support(struct chamelium * chamelium,struct chamelium_port * port)1224 bool chamelium_has_audio_support(struct chamelium *chamelium,
1225 				 struct chamelium_port *port)
1226 {
1227 	xmlrpc_value *res;
1228 	xmlrpc_bool has_support;
1229 
1230 	if (!chamelium_supports_method(chamelium, "GetAudioFormat")) {
1231 		igt_debug("The Chamelium device doesn't support GetAudioFormat\n");
1232 		return false;
1233 	}
1234 
1235 	res = chamelium_rpc(chamelium, port, "HasAudioSupport", "(i)", port->id);
1236 	xmlrpc_read_bool(&chamelium->env, res, &has_support);
1237 	xmlrpc_DECREF(res);
1238 
1239 	return has_support;
1240 }
1241 
1242 /**
1243  * chamelium_get_audio_channel_mapping:
1244  * @chamelium: the Chamelium instance
1245  * @port: the audio port
1246  * @mapping: will be filled with the channel mapping
1247  *
1248  * Obtains the channel mapping for an audio port.
1249  *
1250  * Audio channels are not guaranteed not to be swapped. Users can use the
1251  * channel mapping to match an input channel to a capture channel.
1252  *
1253  * The mapping contains one element per capture channel. Each element indicates
1254  * which input channel the capture channel is mapped to. As a special case, -1
1255  * means that the channel isn't mapped.
1256  */
chamelium_get_audio_channel_mapping(struct chamelium * chamelium,struct chamelium_port * port,int mapping[static CHAMELIUM_MAX_AUDIO_CHANNELS])1257 void chamelium_get_audio_channel_mapping(struct chamelium *chamelium,
1258 					 struct chamelium_port *port,
1259 					 int mapping[static CHAMELIUM_MAX_AUDIO_CHANNELS])
1260 {
1261 	xmlrpc_value *res, *res_channel;
1262 	int res_len, i;
1263 
1264 	res = chamelium_rpc(chamelium, port, "GetAudioChannelMapping", "(i)",
1265 			    port->id);
1266 	res_len = xmlrpc_array_size(&chamelium->env, res);
1267 	igt_assert(res_len == CHAMELIUM_MAX_AUDIO_CHANNELS);
1268 	for (i = 0; i < res_len; i++) {
1269 		xmlrpc_array_read_item(&chamelium->env, res, i, &res_channel);
1270 		xmlrpc_read_int(&chamelium->env, res_channel, &mapping[i]);
1271 		xmlrpc_DECREF(res_channel);
1272 	}
1273 	xmlrpc_DECREF(res);
1274 }
1275 
audio_format_from_xml(struct chamelium * chamelium,xmlrpc_value * res,int * rate,int * channels)1276 static void audio_format_from_xml(struct chamelium *chamelium,
1277 				  xmlrpc_value *res, int *rate, int *channels)
1278 {
1279 	xmlrpc_value *res_type, *res_rate, *res_sample_format, *res_channel;
1280 	char *type, *sample_format;
1281 
1282 	xmlrpc_struct_find_value(&chamelium->env, res, "file_type", &res_type);
1283 	xmlrpc_struct_find_value(&chamelium->env, res, "rate", &res_rate);
1284 	xmlrpc_struct_find_value(&chamelium->env, res, "sample_format", &res_sample_format);
1285 	xmlrpc_struct_find_value(&chamelium->env, res, "channel", &res_channel);
1286 
1287 	xmlrpc_read_string(&chamelium->env, res_type, (const char **) &type);
1288 	igt_assert(strcmp(type, "raw") == 0);
1289 	free(type);
1290 
1291 	xmlrpc_read_string(&chamelium->env, res_sample_format, (const char **) &sample_format);
1292 	igt_assert(strcmp(sample_format, "S32_LE") == 0);
1293 	free(sample_format);
1294 
1295 	if (rate)
1296 		xmlrpc_read_int(&chamelium->env, res_rate, rate);
1297 	if (channels) {
1298 		xmlrpc_read_int(&chamelium->env, res_channel, channels);
1299 		igt_assert(*channels <= CHAMELIUM_MAX_AUDIO_CHANNELS);
1300 	}
1301 
1302 	xmlrpc_DECREF(res_channel);
1303 	xmlrpc_DECREF(res_sample_format);
1304 	xmlrpc_DECREF(res_rate);
1305 	xmlrpc_DECREF(res_type);
1306 }
1307 
1308 /**
1309  * chamelium_get_audio_format:
1310  * @chamelium: the Chamelium instance
1311  * @port: the audio port
1312  * @rate: if non-NULL, will be set to the sample rate in Hz
1313  * @channels: if non-NULL, will be set to the number of channels
1314  *
1315  * Obtains the audio format of the captured data. Users should start sending an
1316  * audio signal to the Chamelium device prior to calling this function.
1317  *
1318  * The captured data is guaranteed to be in the S32_LE format.
1319  */
chamelium_get_audio_format(struct chamelium * chamelium,struct chamelium_port * port,int * rate,int * channels)1320 void chamelium_get_audio_format(struct chamelium *chamelium,
1321 				struct chamelium_port *port,
1322 				int *rate, int *channels)
1323 {
1324 	xmlrpc_value *res;
1325 
1326 	res = chamelium_rpc(chamelium, port, "GetAudioFormat", "(i)",
1327 			    port->id);
1328 	audio_format_from_xml(chamelium, res, rate, channels);
1329 	xmlrpc_DECREF(res);
1330 }
1331 
1332 /**
1333  * chamelium_start_capturing_audio:
1334  * @chamelium: the Chamelium instance
1335  * @port: the port to capture audio from (it must support audio)
1336  * @save_to_file: whether the captured audio data should be saved to a file on
1337  * the Chamelium device
1338  *
1339  * Starts capturing audio from a Chamelium port. To stop the capture, use
1340  * #chamelium_stop_capturing_audio. To retrieve the audio data, either use the
1341  * stream server or enable @save_to_file (the latter is mainly useful for
1342  * debugging purposes).
1343  *
1344  * It isn't possible to capture audio from multiple ports at the same time.
1345  */
chamelium_start_capturing_audio(struct chamelium * chamelium,struct chamelium_port * port,bool save_to_file)1346 void chamelium_start_capturing_audio(struct chamelium *chamelium,
1347 				    struct chamelium_port *port,
1348 				    bool save_to_file)
1349 {
1350 	xmlrpc_value *res;
1351 
1352 	res = chamelium_rpc(chamelium, port, "StartCapturingAudio", "(ib)",
1353 			    port->id, save_to_file);
1354 	xmlrpc_DECREF(res);
1355 }
1356 
1357 /**
1358  * chamelium_stop_capturing_audio:
1359  * @chamelium: the Chamelium instance
1360  * @port: the port from which audio is being captured
1361  *
1362  * Stops capturing audio from a Chamelium port. If
1363  * #chamelium_start_capturing_audio has been called with @save_to_file enabled,
1364  * this function will return a #chamelium_audio_file struct containing details
1365  * about the audio file. Once the caller is done with the struct, they should
1366  * release it with #chamelium_destroy_audio_file.
1367  */
chamelium_stop_capturing_audio(struct chamelium * chamelium,struct chamelium_port * port)1368 struct chamelium_audio_file *chamelium_stop_capturing_audio(struct chamelium *chamelium,
1369 							    struct chamelium_port *port)
1370 {
1371 	xmlrpc_value *res, *res_path, *res_props;
1372 	struct chamelium_audio_file *file = NULL;
1373 	char *path;
1374 
1375 	res = chamelium_rpc(chamelium, NULL, "StopCapturingAudio", "(i)",
1376 			    port->id);
1377 	xmlrpc_array_read_item(&chamelium->env, res, 0, &res_path);
1378 	xmlrpc_array_read_item(&chamelium->env, res, 1, &res_props);
1379 
1380 	xmlrpc_read_string(&chamelium->env, res_path, (const char **) &path);
1381 
1382 	if (strlen(path) > 0) {
1383 		file = calloc(1, sizeof(*file));
1384 		file->path = path;
1385 
1386 		audio_format_from_xml(chamelium, res_props,
1387 				      &file->rate, &file->channels);
1388 	} else {
1389 		free(path);
1390 	}
1391 
1392 	xmlrpc_DECREF(res_props);
1393 	xmlrpc_DECREF(res_path);
1394 	xmlrpc_DECREF(res);
1395 
1396 	return file;
1397 }
1398 
convert_frame_format(pixman_image_t * src,int format)1399 static pixman_image_t *convert_frame_format(pixman_image_t *src,
1400 					    int format)
1401 {
1402 	pixman_image_t *converted;
1403 	int w = pixman_image_get_width(src), h = pixman_image_get_height(src);
1404 
1405 	converted = pixman_image_create_bits(format, w, h, NULL,
1406 					     PIXMAN_FORMAT_BPP(format) / 8 * w);
1407 	pixman_image_composite(PIXMAN_OP_ADD, src, NULL, converted,
1408 			       0, 0, 0, 0, 0, 0, w, h);
1409 
1410 	return converted;
1411 }
1412 
convert_frame_dump_argb32(const struct chamelium_frame_dump * dump)1413 static cairo_surface_t *convert_frame_dump_argb32(const struct chamelium_frame_dump *dump)
1414 {
1415 	cairo_surface_t *dump_surface;
1416 	pixman_image_t *image_bgr;
1417 	pixman_image_t *image_argb;
1418 	int w = dump->width, h = dump->height;
1419 	uint32_t *bits_bgr = (uint32_t *) dump->bgr;
1420 	unsigned char *bits_argb;
1421 	unsigned char *bits_target;
1422 	int size;
1423 
1424 	image_bgr = pixman_image_create_bits(
1425 	    PIXMAN_b8g8r8, w, h, bits_bgr,
1426 	    PIXMAN_FORMAT_BPP(PIXMAN_b8g8r8) / 8 * w);
1427 	image_argb = convert_frame_format(image_bgr, PIXMAN_x8r8g8b8);
1428 	pixman_image_unref(image_bgr);
1429 
1430 	bits_argb = (unsigned char *) pixman_image_get_data(image_argb);
1431 
1432 	dump_surface = cairo_image_surface_create(
1433 	    CAIRO_FORMAT_ARGB32, w, h);
1434 
1435 	bits_target = cairo_image_surface_get_data(dump_surface);
1436 	size = cairo_image_surface_get_stride(dump_surface) * h;
1437 	memcpy(bits_target, bits_argb, size);
1438 	cairo_surface_mark_dirty(dump_surface);
1439 
1440 	pixman_image_unref(image_argb);
1441 
1442 	return dump_surface;
1443 }
1444 
compared_frames_dump(cairo_surface_t * reference,cairo_surface_t * capture,igt_crc_t * reference_crc,igt_crc_t * capture_crc)1445 static void compared_frames_dump(cairo_surface_t *reference,
1446 				 cairo_surface_t *capture,
1447 				 igt_crc_t *reference_crc,
1448 				 igt_crc_t *capture_crc)
1449 {
1450 	char *reference_suffix;
1451 	char *capture_suffix;
1452 	igt_crc_t local_reference_crc;
1453 	igt_crc_t local_capture_crc;
1454 
1455 	igt_assert(reference && capture);
1456 
1457 	if (!reference_crc) {
1458 		chamelium_do_calculate_fb_crc(reference, &local_reference_crc);
1459 		reference_crc = &local_reference_crc;
1460 	}
1461 
1462 	if (!capture_crc) {
1463 		chamelium_do_calculate_fb_crc(reference, &local_capture_crc);
1464 		capture_crc = &local_capture_crc;
1465 	}
1466 
1467 	reference_suffix = igt_crc_to_string_extended(reference_crc, '-', 2);
1468 	capture_suffix = igt_crc_to_string_extended(capture_crc, '-', 2);
1469 
1470 	/* Write reference and capture frames to png. */
1471 	igt_write_compared_frames_to_png(reference, capture, reference_suffix,
1472 					 capture_suffix);
1473 
1474 	free(reference_suffix);
1475 	free(capture_suffix);
1476 }
1477 
1478 /**
1479  * chamelium_assert_frame_eq:
1480  * @chamelium: The chamelium instance the frame dump belongs to
1481  * @dump: The chamelium frame dump to check
1482  * @fb: The framebuffer to check against
1483  *
1484  * Asserts that the image contained in the chamelium frame dump is identical to
1485  * the given framebuffer. Useful for scenarios where pre-calculating CRCs might
1486  * not be ideal.
1487  */
chamelium_assert_frame_eq(const struct chamelium * chamelium,const struct chamelium_frame_dump * dump,struct igt_fb * fb)1488 void chamelium_assert_frame_eq(const struct chamelium *chamelium,
1489 			       const struct chamelium_frame_dump *dump,
1490 			       struct igt_fb *fb)
1491 {
1492 	cairo_surface_t *fb_surface;
1493 	pixman_image_t *reference_src, *reference_bgr;
1494 	int w = dump->width, h = dump->height;
1495 	bool eq;
1496 
1497 	/* Get the cairo surface for the framebuffer */
1498 	fb_surface = igt_get_cairo_surface(chamelium->drm_fd, fb);
1499 
1500 	/*
1501 	 * Convert the reference image into the same format as the chamelium
1502 	 * image
1503 	 */
1504 	reference_src = pixman_image_create_bits(
1505 	    PIXMAN_x8r8g8b8, w, h,
1506 	    (void*)cairo_image_surface_get_data(fb_surface),
1507 	    cairo_image_surface_get_stride(fb_surface));
1508 	reference_bgr = convert_frame_format(reference_src, PIXMAN_b8g8r8);
1509 	pixman_image_unref(reference_src);
1510 
1511 	/* Now do the actual comparison */
1512 	eq = memcmp(dump->bgr, pixman_image_get_data(reference_bgr),
1513 		    dump->size) == 0;
1514 
1515 	pixman_image_unref(reference_bgr);
1516 
1517 	igt_fail_on_f(!eq,
1518 		      "Chamelium frame dump didn't match reference image\n");
1519 }
1520 
1521 /**
1522  * chamelium_assert_crc_eq_or_dump:
1523  * @chamelium: The chamelium instance the frame dump belongs to
1524  * @reference_crc: The CRC for the reference frame
1525  * @capture_crc: The CRC for the captured frame
1526  * @fb: pointer to an #igt_fb structure
1527  *
1528  * Asserts that the CRC provided for both the reference and the captured frame
1529  * are identical. If they are not, this grabs the captured frame and saves it
1530  * along with the reference to a png file.
1531  */
chamelium_assert_crc_eq_or_dump(struct chamelium * chamelium,igt_crc_t * reference_crc,igt_crc_t * capture_crc,struct igt_fb * fb,int index)1532 void chamelium_assert_crc_eq_or_dump(struct chamelium *chamelium,
1533 				     igt_crc_t *reference_crc,
1534 				     igt_crc_t *capture_crc, struct igt_fb *fb,
1535 				     int index)
1536 {
1537 	struct chamelium_frame_dump *frame;
1538 	cairo_surface_t *reference;
1539 	cairo_surface_t *capture;
1540 	bool eq;
1541 
1542 	igt_debug("Reference CRC: %s\n", igt_crc_to_string(reference_crc));
1543 	igt_debug("Captured CRC: %s\n", igt_crc_to_string(capture_crc));
1544 
1545 	eq = igt_check_crc_equal(reference_crc, capture_crc);
1546 	if (!eq && igt_frame_dump_is_enabled()) {
1547 		/* Convert the reference framebuffer to cairo. */
1548 		reference = igt_get_cairo_surface(chamelium->drm_fd, fb);
1549 
1550 		/* Grab the captured frame from the Chamelium. */
1551 		frame = chamelium_read_captured_frame(chamelium, index);
1552 		igt_assert(frame);
1553 
1554 		/* Convert the captured frame to cairo. */
1555 		capture = convert_frame_dump_argb32(frame);
1556 		igt_assert(capture);
1557 
1558 		compared_frames_dump(reference, capture, reference_crc,
1559 				     capture_crc);
1560 
1561 		cairo_surface_destroy(reference);
1562 		cairo_surface_destroy(capture);
1563 		chamelium_destroy_frame_dump(frame);
1564 	}
1565 
1566 	igt_assert(eq);
1567 }
1568 
1569 /**
1570  * chamelium_assert_frame_match_or_dump:
1571  * @chamelium: The chamelium instance the frame dump belongs to
1572  * @frame: The chamelium frame dump to match
1573  * @fb: pointer to an #igt_fb structure
1574  * @check: the type of frame matching check to use
1575  *
1576  * Asserts that the provided captured frame matches the reference frame from
1577  * the framebuffer. If they do not, this saves the reference and captured frames
1578  * to a png file.
1579  */
chamelium_assert_frame_match_or_dump(struct chamelium * chamelium,struct chamelium_port * port,const struct chamelium_frame_dump * frame,struct igt_fb * fb,enum chamelium_check check)1580 void chamelium_assert_frame_match_or_dump(struct chamelium *chamelium,
1581 					  struct chamelium_port *port,
1582 					  const struct chamelium_frame_dump *frame,
1583 					  struct igt_fb *fb,
1584 					  enum chamelium_check check)
1585 {
1586 	cairo_surface_t *reference;
1587 	cairo_surface_t *capture;
1588 	igt_crc_t *reference_crc;
1589 	igt_crc_t *capture_crc;
1590 	bool match;
1591 
1592 	/* Grab the reference frame from framebuffer */
1593 	reference = igt_get_cairo_surface(chamelium->drm_fd, fb);
1594 
1595 	/* Grab the captured frame from chamelium */
1596 	capture = convert_frame_dump_argb32(frame);
1597 
1598 	switch (check) {
1599 	case CHAMELIUM_CHECK_ANALOG:
1600 		match = igt_check_analog_frame_match(reference, capture);
1601 		break;
1602 	case CHAMELIUM_CHECK_CHECKERBOARD:
1603 		match = igt_check_checkerboard_frame_match(reference, capture);
1604 		break;
1605 	default:
1606 		igt_assert(false);
1607 	}
1608 
1609 	if (!match && igt_frame_dump_is_enabled()) {
1610 		reference_crc = malloc(sizeof(igt_crc_t));
1611 		igt_assert(reference_crc);
1612 
1613 		/* Calculate the reference frame CRC. */
1614 		chamelium_do_calculate_fb_crc(reference, reference_crc);
1615 
1616 		/* Get the captured frame CRC from the Chamelium. */
1617 		capture_crc = chamelium_get_crc_for_area(chamelium, port, 0, 0,
1618 							 0, 0);
1619 		igt_assert(capture_crc);
1620 
1621 		compared_frames_dump(reference, capture, reference_crc,
1622 				     capture_crc);
1623 
1624 		free(reference_crc);
1625 		free(capture_crc);
1626 	}
1627 
1628 	igt_assert(match);
1629 
1630 	cairo_surface_destroy(reference);
1631 	cairo_surface_destroy(capture);
1632 }
1633 
1634 /**
1635  * chamelium_analog_frame_crop:
1636  * @chamelium: The Chamelium instance to use
1637  * @dump: The chamelium frame dump to crop
1638  * @width: The cropped frame width
1639  * @height: The cropped frame height
1640  *
1641  * Detects the corners of a chamelium frame and crops it to the requested
1642  * width/height. This is useful for VGA frame dumps that also contain the
1643  * pixels dumped during the blanking intervals.
1644  *
1645  * The detection is done on a brightness-threshold-basis, that is adapted
1646  * to the reference frame used by i-g-t. It may not be as relevant for other
1647  * frames.
1648  */
chamelium_crop_analog_frame(struct chamelium_frame_dump * dump,int width,int height)1649 void chamelium_crop_analog_frame(struct chamelium_frame_dump *dump, int width,
1650 				 int height)
1651 {
1652 	unsigned char *bgr;
1653 	unsigned char *p;
1654 	unsigned char *q;
1655 	int top, left;
1656 	int x, y, xx, yy;
1657 	int score;
1658 
1659 	if (dump->width == width && dump->height == height)
1660 		return;
1661 
1662 	/* Start with the most bottom-right position. */
1663 	top = dump->height - height;
1664 	left = dump->width - width;
1665 
1666 	igt_assert(top >= 0 && left >= 0);
1667 
1668 	igt_debug("Cropping analog frame from %dx%d to %dx%d\n", dump->width,
1669 		  dump->height, width, height);
1670 
1671 	/* Detect the top-left corner of the frame. */
1672 	for (x = 0; x < dump->width; x++) {
1673 		for (y = 0; y < dump->height; y++) {
1674 			p = &dump->bgr[(x + y * dump->width) * 3];
1675 
1676 			/* Detect significantly bright pixels. */
1677 			if (p[0] < 50 && p[1] < 50 && p[2] < 50)
1678 				continue;
1679 
1680 			/*
1681 			 * Make sure close-by pixels are also significantly
1682 			 * bright.
1683 			 */
1684 			score = 0;
1685 			for (xx = x; xx < x + 10; xx++) {
1686 				for (yy = y; yy < y + 10; yy++) {
1687 					p = &dump->bgr[(xx + yy * dump->width) * 3];
1688 
1689 					if (p[0] > 50 && p[1] > 50 && p[2] > 50)
1690 						score++;
1691 				}
1692 			}
1693 
1694 			/* Not enough pixels are significantly bright. */
1695 			if (score < 25)
1696 				continue;
1697 
1698 			if (x < left)
1699 				left = x;
1700 
1701 			if (y < top)
1702 				top = y;
1703 
1704 			if (left == x || top == y)
1705 				continue;
1706 		}
1707 	}
1708 
1709 	igt_debug("Detected analog frame edges at %dx%d\n", left, top);
1710 
1711 	/* Crop the frame given the detected top-left corner. */
1712 	bgr = malloc(width * height * 3);
1713 
1714 	for (y = 0; y < height; y++) {
1715 		p = &dump->bgr[(left + (top + y) * dump->width) * 3];
1716 		q = &bgr[(y * width) * 3];
1717 		memcpy(q, p, width * 3);
1718 	}
1719 
1720 	free(dump->bgr);
1721 	dump->width = width;
1722 	dump->height = height;
1723 	dump->bgr = bgr;
1724 }
1725 
1726 /**
1727  * chamelium_get_frame_limit:
1728  * @chamelium: The Chamelium instance to use
1729  * @port: The port to check the frame limit on
1730  * @w: The width of the area to get the capture frame limit for, or %0 for the
1731  * whole display
1732  * @h: The height of the area to get the capture frame limit for, or %0 for the
1733  * whole display
1734  *
1735  * Gets the max number of frames we can capture with the Chamelium for the given
1736  * resolution.
1737  *
1738  * Returns: The number of the max number of frames we can capture
1739  */
chamelium_get_frame_limit(struct chamelium * chamelium,struct chamelium_port * port,int w,int h)1740 int chamelium_get_frame_limit(struct chamelium *chamelium,
1741 			      struct chamelium_port *port,
1742 			      int w, int h)
1743 {
1744 	xmlrpc_value *res;
1745 	int ret;
1746 
1747 	if (!w && !h)
1748 		chamelium_port_get_resolution(chamelium, port, &w, &h);
1749 
1750 	res = chamelium_rpc(chamelium, port, "GetMaxFrameLimit", "(iii)",
1751 			    port->id, w, h);
1752 
1753 	xmlrpc_read_int(&chamelium->env, res, &ret);
1754 	xmlrpc_DECREF(res);
1755 
1756 	return ret;
1757 }
1758 
chamelium_xrgb_hash16(const unsigned char * buffer,int width,int height,int k,int m)1759 static uint32_t chamelium_xrgb_hash16(const unsigned char *buffer, int width,
1760 				      int height, int k, int m)
1761 {
1762 	unsigned char r, g, b;
1763 	uint64_t sum = 0;
1764 	uint64_t count = 0;
1765 	uint64_t value;
1766 	uint32_t hash;
1767 	int index;
1768 	int i;
1769 
1770 	for (i=0; i < width * height; i++) {
1771 		if ((i % m) != k)
1772 			continue;
1773 
1774 		index = i * 4;
1775 
1776 		r = buffer[index + 2];
1777 		g = buffer[index + 1];
1778 		b = buffer[index + 0];
1779 
1780 		value = r | (g << 8) | (b << 16);
1781 		sum += ++count * value;
1782 	}
1783 
1784 	hash = ((sum >> 0) ^ (sum >> 16) ^ (sum >> 32) ^ (sum >> 48)) & 0xffff;
1785 
1786 	return hash;
1787 }
1788 
chamelium_do_calculate_fb_crc(cairo_surface_t * fb_surface,igt_crc_t * out)1789 static void chamelium_do_calculate_fb_crc(cairo_surface_t *fb_surface,
1790 					  igt_crc_t *out)
1791 {
1792 	unsigned char *buffer;
1793 	int n = 4;
1794 	int w, h;
1795 	int i, j;
1796 
1797 	buffer = cairo_image_surface_get_data(fb_surface);
1798 	w = cairo_image_surface_get_width(fb_surface);
1799 	h = cairo_image_surface_get_height(fb_surface);
1800 
1801 	for (i = 0; i < n; i++) {
1802 		j = n - i - 1;
1803 		out->crc[i] = chamelium_xrgb_hash16(buffer, w, h, j, n);
1804 	}
1805 
1806 	out->n_words = n;
1807 }
1808 
1809 /**
1810  * chamelium_calculate_fb_crc:
1811  * @fd: The drm file descriptor
1812  * @fb: The framebuffer to calculate the CRC for
1813  *
1814  * Calculates the CRC for the provided framebuffer, using the Chamelium's CRC
1815  * algorithm. This calculates the CRC in a synchronous fashion.
1816  *
1817  * Returns: The calculated CRC
1818  */
chamelium_calculate_fb_crc(int fd,struct igt_fb * fb)1819 igt_crc_t *chamelium_calculate_fb_crc(int fd, struct igt_fb *fb)
1820 {
1821 	igt_crc_t *ret = calloc(1, sizeof(igt_crc_t));
1822 	cairo_surface_t *fb_surface;
1823 
1824 	/* Get the cairo surface for the framebuffer */
1825 	fb_surface = igt_get_cairo_surface(fd, fb);
1826 
1827 	chamelium_do_calculate_fb_crc(fb_surface, ret);
1828 
1829 	cairo_surface_destroy(fb_surface);
1830 
1831 	return ret;
1832 }
1833 
chamelium_calculate_fb_crc_async_work(void * data)1834 static void *chamelium_calculate_fb_crc_async_work(void *data)
1835 {
1836 	struct chamelium_fb_crc_async_data *fb_crc;
1837 
1838 	fb_crc = (struct chamelium_fb_crc_async_data *) data;
1839 
1840 	chamelium_do_calculate_fb_crc(fb_crc->fb_surface, fb_crc->ret);
1841 
1842 	return NULL;
1843 }
1844 
1845 /**
1846  * chamelium_calculate_fb_crc_launch:
1847  * @fd: The drm file descriptor
1848  * @fb: The framebuffer to calculate the CRC for
1849  *
1850  * Launches the CRC calculation for the provided framebuffer, using the
1851  * Chamelium's CRC algorithm. This calculates the CRC in an asynchronous
1852  * fashion.
1853  *
1854  * The returned structure should be passed to a subsequent call to
1855  * chamelium_calculate_fb_crc_result. It should not be freed.
1856  *
1857  * Returns: An intermediate structure for the CRC calculation work.
1858  */
chamelium_calculate_fb_crc_async_start(int fd,struct igt_fb * fb)1859 struct chamelium_fb_crc_async_data *chamelium_calculate_fb_crc_async_start(int fd,
1860 									   struct igt_fb *fb)
1861 {
1862 	struct chamelium_fb_crc_async_data *fb_crc;
1863 
1864 	fb_crc = calloc(1, sizeof(struct chamelium_fb_crc_async_data));
1865 	fb_crc->ret = calloc(1, sizeof(igt_crc_t));
1866 
1867 	/* Get the cairo surface for the framebuffer */
1868 	fb_crc->fb_surface = igt_get_cairo_surface(fd, fb);
1869 
1870 	pthread_create(&fb_crc->thread_id, NULL,
1871 		       chamelium_calculate_fb_crc_async_work, fb_crc);
1872 
1873 	return fb_crc;
1874 }
1875 
1876 /**
1877  * chamelium_calculate_fb_crc_result:
1878  * @fb_crc: An intermediate structure with thread-related information
1879  *
1880  * Blocks until the asynchronous CRC calculation is finished, and then returns
1881  * its result.
1882  *
1883  * Returns: The calculated CRC
1884  */
chamelium_calculate_fb_crc_async_finish(struct chamelium_fb_crc_async_data * fb_crc)1885 igt_crc_t *chamelium_calculate_fb_crc_async_finish(struct chamelium_fb_crc_async_data *fb_crc)
1886 {
1887 	igt_crc_t *ret;
1888 
1889 	pthread_join(fb_crc->thread_id, NULL);
1890 
1891 	ret = fb_crc->ret;
1892 	free(fb_crc);
1893 
1894 	return ret;
1895 }
1896 
chamelium_get_port_type(struct chamelium * chamelium,struct chamelium_port * port)1897 static unsigned int chamelium_get_port_type(struct chamelium *chamelium,
1898 					    struct chamelium_port *port)
1899 {
1900 	xmlrpc_value *res;
1901 	const char *port_type_str;
1902 	unsigned int port_type;
1903 
1904 	res = chamelium_rpc(chamelium, NULL, "GetConnectorType",
1905 			    "(i)", port->id);
1906 
1907 	xmlrpc_read_string(&chamelium->env, res, &port_type_str);
1908 	igt_debug("Port %d is of type '%s'\n", port->id, port_type_str);
1909 
1910 	if (strcmp(port_type_str, "DP") == 0)
1911 		port_type = DRM_MODE_CONNECTOR_DisplayPort;
1912 	else if (strcmp(port_type_str, "HDMI") == 0)
1913 		port_type = DRM_MODE_CONNECTOR_HDMIA;
1914 	else if (strcmp(port_type_str, "VGA") == 0)
1915 		port_type = DRM_MODE_CONNECTOR_VGA;
1916 	else
1917 		port_type = DRM_MODE_CONNECTOR_Unknown;
1918 
1919 	free((void*)port_type_str);
1920 	xmlrpc_DECREF(res);
1921 
1922 	return port_type;
1923 }
1924 
chamelium_has_video_support(struct chamelium * chamelium,int port_id)1925 static bool chamelium_has_video_support(struct chamelium *chamelium,
1926 					int port_id)
1927 {
1928 	xmlrpc_value *res;
1929 	int has_video_support;
1930 
1931 	res = chamelium_rpc(chamelium, NULL, "HasVideoSupport", "(i)", port_id);
1932 	xmlrpc_read_bool(&chamelium->env, res, &has_video_support);
1933 	xmlrpc_DECREF(res);
1934 
1935 	return has_video_support;
1936 }
1937 
1938 /**
1939  * chamelium_get_video_ports: retrieve a list of video port IDs
1940  *
1941  * Returns: the number of video port IDs
1942  */
chamelium_get_video_ports(struct chamelium * chamelium,int port_ids[static CHAMELIUM_MAX_PORTS])1943 static size_t chamelium_get_video_ports(struct chamelium *chamelium,
1944 					int port_ids[static CHAMELIUM_MAX_PORTS])
1945 {
1946 	xmlrpc_value *res, *res_port;
1947 	int res_len, i, port_id;
1948 	size_t port_ids_len = 0;
1949 
1950 	res = chamelium_rpc(chamelium, NULL, "GetSupportedInputs", "()");
1951 	res_len = xmlrpc_array_size(&chamelium->env, res);
1952 	for (i = 0; i < res_len; i++) {
1953 		xmlrpc_array_read_item(&chamelium->env, res, i, &res_port);
1954 		xmlrpc_read_int(&chamelium->env, res_port, &port_id);
1955 		xmlrpc_DECREF(res_port);
1956 
1957 		if (!chamelium_has_video_support(chamelium, port_id))
1958 			continue;
1959 
1960 		igt_assert(port_ids_len < CHAMELIUM_MAX_PORTS);
1961 		port_ids[port_ids_len] = port_id;
1962 		port_ids_len++;
1963 	}
1964 	xmlrpc_DECREF(res);
1965 
1966 	return port_ids_len;
1967 }
1968 
chamelium_read_port_mappings(struct chamelium * chamelium,int drm_fd)1969 static bool chamelium_read_port_mappings(struct chamelium *chamelium,
1970 					 int drm_fd)
1971 {
1972 	drmModeRes *res;
1973 	drmModeConnector *connector;
1974 	struct chamelium_port *port;
1975 	GError *error = NULL;
1976 	char **group_list;
1977 	char *group, *map_name;
1978 	int port_i, i, j;
1979 	bool ret = true;
1980 
1981 	res = drmModeGetResources(drm_fd);
1982 	if (!res)
1983 		return false;
1984 
1985 	group_list = g_key_file_get_groups(igt_key_file, NULL);
1986 
1987 	/* Count how many connector mappings are specified in the config */
1988 	for (i = 0; group_list[i] != NULL; i++) {
1989 		if (strstr(group_list[i], "Chamelium:"))
1990 			chamelium->port_count++;
1991 	}
1992 	igt_assert(chamelium->port_count <= CHAMELIUM_MAX_PORTS);
1993 
1994 	port_i = 0;
1995 	for (i = 0; group_list[i] != NULL; i++) {
1996 		group = group_list[i];
1997 
1998 		if (!strstr(group, "Chamelium:"))
1999 			continue;
2000 
2001 		map_name = group + (sizeof("Chamelium:") - 1);
2002 
2003 		port = &chamelium->ports[port_i++];
2004 		port->name = strdup(map_name);
2005 		port->id = g_key_file_get_integer(igt_key_file, group,
2006 						  "ChameliumPortID",
2007 						  &error);
2008 		if (!port->id) {
2009 			igt_warn("Failed to read chamelium port ID for %s: %s\n",
2010 				 map_name, error->message);
2011 			ret = false;
2012 			goto out;
2013 		}
2014 
2015 		port->type = chamelium_get_port_type(chamelium, port);
2016 		if (port->type == DRM_MODE_CONNECTOR_Unknown) {
2017 			igt_warn("Unable to retrieve the physical port type from the Chamelium for '%s'\n",
2018 				 map_name);
2019 			ret = false;
2020 			goto out;
2021 		}
2022 
2023 		for (j = 0;
2024 		     j < res->count_connectors && !port->connector_id;
2025 		     j++) {
2026 			char name[50];
2027 
2028 			connector = drmModeGetConnectorCurrent(
2029 			    drm_fd, res->connectors[j]);
2030 
2031 			/* We have to generate the connector name on our own */
2032 			snprintf(name, 50, "%s-%u",
2033 				 kmstest_connector_type_str(connector->connector_type),
2034 				 connector->connector_type_id);
2035 
2036 			if (strcmp(name, map_name) == 0)
2037 				port->connector_id = connector->connector_id;
2038 
2039 			drmModeFreeConnector(connector);
2040 		}
2041 		if (!port->connector_id) {
2042 			igt_warn("No connector found with name '%s'\n",
2043 				 map_name);
2044 			ret = false;
2045 			goto out;
2046 		}
2047 
2048 		igt_debug("Port '%s' with physical type '%s' mapped to Chamelium port %d\n",
2049 			  map_name, kmstest_connector_type_str(port->type),
2050 			  port->id);
2051 	}
2052 
2053 out:
2054 	g_strfreev(group_list);
2055 	drmModeFreeResources(res);
2056 
2057 	return ret;
2058 }
2059 
port_id_from_edid(int drm_fd,drmModeConnector * connector)2060 static int port_id_from_edid(int drm_fd, drmModeConnector *connector)
2061 {
2062 	int port_id = -1;
2063 	bool ok;
2064 	uint64_t edid_blob_id;
2065 	drmModePropertyBlobRes *edid_blob;
2066 	const struct edid *edid;
2067 	char mfg[3];
2068 
2069 	if (connector->connection != DRM_MODE_CONNECTED) {
2070 		igt_debug("Skipping auto-discovery for connector %s-%d: "
2071 			  "connector status is not connected\n",
2072 			  kmstest_connector_type_str(connector->connector_type),
2073 			  connector->connector_type_id);
2074 		return -1;
2075 	}
2076 
2077 	ok = kmstest_get_property(drm_fd, connector->connector_id,
2078 				  DRM_MODE_OBJECT_CONNECTOR, "EDID",
2079 				  NULL, &edid_blob_id, NULL);
2080 	if (!ok || !edid_blob_id) {
2081 		igt_debug("Skipping auto-discovery for connector %s-%d: "
2082 			  "missing the EDID property\n",
2083 			  kmstest_connector_type_str(connector->connector_type),
2084 			  connector->connector_type_id);
2085 		return -1;
2086 	}
2087 
2088 	edid_blob = drmModeGetPropertyBlob(drm_fd, edid_blob_id);
2089 	igt_assert(edid_blob);
2090 
2091 	edid = (const struct edid *) edid_blob->data;
2092 
2093 	edid_get_mfg(edid, mfg);
2094 	if (memcmp(mfg, "IGT", 3) != 0) {
2095 		igt_debug("Skipping connector %s-%d for auto-discovery: "
2096 			  "manufacturer is %.3s, not IGT\n",
2097 			  kmstest_connector_type_str(connector->connector_type),
2098 			  connector->connector_type_id, mfg);
2099 		goto out;
2100 	}
2101 
2102 	if (edid->prod_code[0] != 'C' || edid->prod_code[1] != 'H') {
2103 		igt_warn("Invalid EDID for IGT connector %s-%d: "
2104 			  "invalid product code\n",
2105 			  kmstest_connector_type_str(connector->connector_type),
2106 			  connector->connector_type_id);
2107 		goto out;
2108 	}
2109 
2110 	port_id = *(uint32_t *) &edid->serial;
2111 	igt_debug("Auto-discovery mapped connector %s-%d to Chamelium "
2112 		  "port ID %d\n",
2113 		  kmstest_connector_type_str(connector->connector_type),
2114 		  connector->connector_type_id, port_id);
2115 
2116 out:
2117 	drmModeFreePropertyBlob(edid_blob);
2118 	return port_id;
2119 }
2120 
2121 /**
2122  * chamelium_autodiscover: automagically discover the Chamelium port mapping
2123  *
2124  * The Chamelium API uses port IDs wheras the Device Under Test uses DRM
2125  * connectors. We need to know which Chamelium port is plugged to a given DRM
2126  * connector. This has typically been done via a configuration file in the
2127  * past (see #chamelium_read_port_mappings), but this function provides an
2128  * automatic way to do it.
2129  *
2130  * We will plug all Chamelium ports with a different EDID on each. Then we'll
2131  * read the EDID on each DRM connector and infer the Chamelium port ID.
2132  */
chamelium_autodiscover(struct chamelium * chamelium,int drm_fd)2133 static bool chamelium_autodiscover(struct chamelium *chamelium, int drm_fd)
2134 {
2135 	int candidate_ports[CHAMELIUM_MAX_PORTS];
2136 	size_t candidate_ports_len;
2137 	drmModeRes *res;
2138 	drmModeConnector *connector;
2139 	struct chamelium_port *port;
2140 	size_t i, j, port_count;
2141 	int port_id;
2142 	uint32_t conn_id;
2143 	struct chamelium_edid *edid;
2144 	bool found;
2145 	uint32_t discovered_conns[CHAMELIUM_MAX_PORTS] = {0};
2146 	char conn_name[64];
2147 	struct timespec start;
2148 	uint64_t elapsed_ns;
2149 
2150 	candidate_ports_len = chamelium_get_video_ports(chamelium,
2151 							candidate_ports);
2152 
2153 	igt_debug("Starting Chamelium port auto-discovery on %zu ports\n",
2154 		  candidate_ports_len);
2155 	igt_gettime(&start);
2156 
2157 	edid = chamelium_new_edid(chamelium, igt_kms_get_base_edid());
2158 
2159 	/* Set EDID and plug ports we want to auto-discover */
2160 	port_count = chamelium->port_count;
2161 	for (i = 0; i < candidate_ports_len; i++) {
2162 		port_id = candidate_ports[i];
2163 
2164 		/* Get or add a chamelium_port slot */
2165 		port = NULL;
2166 		for (j = 0; j < chamelium->port_count; j++) {
2167 			if (chamelium->ports[j].id == port_id) {
2168 				port = &chamelium->ports[j];
2169 				break;
2170 			}
2171 		}
2172 		if (!port) {
2173 			igt_assert(port_count < CHAMELIUM_MAX_PORTS);
2174 			port = &chamelium->ports[port_count];
2175 			port_count++;
2176 
2177 			port->id = port_id;
2178 		}
2179 
2180 		chamelium_port_set_edid(chamelium, port, edid);
2181 		chamelium_plug(chamelium, port);
2182 	}
2183 
2184 	/* Reprobe connectors and build the mapping */
2185 	res = drmModeGetResources(drm_fd);
2186 	if (!res)
2187 		return false;
2188 
2189 	for (i = 0; i < res->count_connectors; i++) {
2190 		conn_id = res->connectors[i];
2191 
2192 		/* Read the EDID and parse the Chamelium port ID we stored
2193 		 * there. */
2194 		connector = drmModeGetConnector(drm_fd, res->connectors[i]);
2195 		port_id = port_id_from_edid(drm_fd, connector);
2196 		drmModeFreeConnector(connector);
2197 		if (port_id < 0)
2198 			continue;
2199 
2200 		/* If we already have a mapping from the config file, check
2201 		 * that it's consistent. */
2202 		found = false;
2203 		for (j = 0; j < chamelium->port_count; j++) {
2204 			port = &chamelium->ports[j];
2205 			if (port->connector_id == conn_id) {
2206 				found = true;
2207 				igt_assert_f(port->id == port_id,
2208 					     "Inconsistency detected in .igtrc: "
2209 					     "connector %s is configured with "
2210 					     "Chamelium port %d, but is "
2211 					     "connected to port %d\n",
2212 					     port->name, port->id, port_id);
2213 				break;
2214 			}
2215 		}
2216 		if (found)
2217 			continue;
2218 
2219 		/* We got a new mapping */
2220 		found = false;
2221 		for (j = 0; j < candidate_ports_len; j++) {
2222 			if (port_id == candidate_ports[j]) {
2223 				found = true;
2224 				discovered_conns[j] = conn_id;
2225 				break;
2226 			}
2227 		}
2228 		igt_assert_f(found, "Auto-discovered a port (%d) we haven't "
2229 			     "setup\n", port_id);
2230 	}
2231 
2232 	drmModeFreeResources(res);
2233 
2234 	/* We now have a Chamelium port ID ↔ DRM connector ID mapping:
2235 	 * candidate_ports contains the Chamelium port IDs and
2236 	 * discovered_conns contains the DRM connector IDs. */
2237 	for (i = 0; i < candidate_ports_len; i++) {
2238 		port_id = candidate_ports[i];
2239 		conn_id = discovered_conns[i];
2240 		if (!conn_id) {
2241 			continue;
2242 		}
2243 
2244 		port = &chamelium->ports[chamelium->port_count];
2245 		chamelium->port_count++;
2246 
2247 		port->id = port_id;
2248 		port->type = chamelium_get_port_type(chamelium, port);
2249 		port->connector_id = conn_id;
2250 
2251 		connector = drmModeGetConnectorCurrent(drm_fd, conn_id);
2252 		snprintf(conn_name, sizeof(conn_name), "%s-%u",
2253 			 kmstest_connector_type_str(connector->connector_type),
2254 			 connector->connector_type_id);
2255 		drmModeFreeConnector(connector);
2256 		port->name = strdup(conn_name);
2257 	}
2258 
2259 	elapsed_ns = igt_nsec_elapsed(&start);
2260 	igt_debug("Auto-discovery took %fms\n",
2261 		  (float) elapsed_ns / (1000 * 1000));
2262 
2263 	return true;
2264 }
2265 
chamelium_read_config(struct chamelium * chamelium,int drm_fd)2266 static bool chamelium_read_config(struct chamelium *chamelium, int drm_fd)
2267 {
2268 	GError *error = NULL;
2269 
2270 	if (!igt_key_file) {
2271 		igt_warn("No configuration file available for chamelium\n");
2272 		return false;
2273 	}
2274 
2275 	chamelium->url = g_key_file_get_string(igt_key_file, "Chamelium", "URL",
2276 					       &error);
2277 	if (!chamelium->url) {
2278 		igt_warn("Couldn't read chamelium URL from config file: %s\n",
2279 			 error->message);
2280 		return false;
2281 	}
2282 
2283 	if (!chamelium_read_port_mappings(chamelium, drm_fd)) {
2284 		return false;
2285 	}
2286 	return chamelium_autodiscover(chamelium, drm_fd);
2287 }
2288 
2289 /**
2290  * chamelium_reset:
2291  * @chamelium: The Chamelium instance to use
2292  *
2293  * Resets the chamelium's IO board. As well, this also has the effect of
2294  * causing all of the chamelium ports to get set to unplugged
2295  */
chamelium_reset(struct chamelium * chamelium)2296 void chamelium_reset(struct chamelium *chamelium)
2297 {
2298 	igt_debug("Resetting the chamelium\n");
2299 	xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Reset", "()"));
2300 }
2301 
chamelium_exit_handler(int sig)2302 static void chamelium_exit_handler(int sig)
2303 {
2304 	igt_debug("Deinitializing Chamelium\n");
2305 
2306 	if (cleanup_instance)
2307 		chamelium_deinit(cleanup_instance);
2308 }
2309 
2310 /**
2311  * chamelium_init:
2312  * @chamelium: The Chamelium instance to use
2313  * @drm_fd: a display initialized with #igt_display_require
2314  *
2315  * Sets up a connection with a chamelium, using the URL specified in the
2316  * Chamelium configuration. This must be called first before trying to use the
2317  * chamelium.
2318  *
2319  * If we fail to establish a connection with the chamelium, fail to find a
2320  * configured connector, etc. we fail the current test.
2321  *
2322  * Returns: A newly initialized chamelium struct, or NULL on error
2323  */
chamelium_init(int drm_fd)2324 struct chamelium *chamelium_init(int drm_fd)
2325 {
2326 	struct chamelium *chamelium = malloc(sizeof(struct chamelium));
2327 
2328 	if (!chamelium)
2329 		return NULL;
2330 
2331 	/* A chamelium instance was set up previously, so clean it up before
2332 	 * starting a new one
2333 	 */
2334 	if (cleanup_instance)
2335 		chamelium_deinit(cleanup_instance);
2336 
2337 	memset(chamelium, 0, sizeof(*chamelium));
2338 	chamelium->drm_fd = drm_fd;
2339 	igt_list_init(&chamelium->edids);
2340 
2341 	/* Setup the libxmlrpc context */
2342 	xmlrpc_env_init(&chamelium->env);
2343 	xmlrpc_client_setup_global_const(&chamelium->env);
2344 	xmlrpc_client_create(&chamelium->env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE,
2345 			     PACKAGE_VERSION, NULL, 0, &chamelium->client);
2346 	if (chamelium->env.fault_occurred) {
2347 		igt_debug("Failed to init xmlrpc: %s\n",
2348 			  chamelium->env.fault_string);
2349 		goto error;
2350 	}
2351 
2352 	if (!chamelium_read_config(chamelium, drm_fd))
2353 		goto error;
2354 
2355 	cleanup_instance = chamelium;
2356 	igt_install_exit_handler(chamelium_exit_handler);
2357 
2358 	return chamelium;
2359 
2360 error:
2361 	xmlrpc_env_clean(&chamelium->env);
2362 	free(chamelium);
2363 
2364 	return NULL;
2365 }
2366 
2367 /**
2368  * chamelium_deinit:
2369  * @chamelium: The Chamelium instance to use
2370  *
2371  * Frees the resources used by a connection to the chamelium that was set up
2372  * with #chamelium_init. As well, this function restores the state of the
2373  * chamelium like it was before calling #chamelium_init. This function is also
2374  * called as an exit handler, so users only need to call manually if they don't
2375  * want the chamelium interfering with other tests in the same file.
2376  */
chamelium_deinit(struct chamelium * chamelium)2377 void chamelium_deinit(struct chamelium *chamelium)
2378 {
2379 	int i;
2380 	struct chamelium_edid *pos, *tmp;
2381 
2382 	/* We want to make sure we leave all of the ports plugged in, since
2383 	 * testing setups requiring multiple monitors are probably using the
2384 	 * chamelium to provide said monitors
2385 	 */
2386 	chamelium_reset(chamelium);
2387 	for (i = 0; i < chamelium->port_count; i++)
2388 		chamelium_plug(chamelium, &chamelium->ports[i]);
2389 
2390 	/* Destroy any EDIDs we created to make sure we don't leak them */
2391 	igt_list_for_each_safe(pos, tmp, &chamelium->edids, link) {
2392 		for (i = 0; i < CHAMELIUM_MAX_PORTS; i++) {
2393 			if (pos->ids[i])
2394 				chamelium_destroy_edid(chamelium, pos->ids[i]);
2395 			free(pos->raw[i]);
2396 		}
2397 		free(pos->base);
2398 		free(pos);
2399 	}
2400 
2401 	xmlrpc_client_destroy(chamelium->client);
2402 	xmlrpc_env_clean(&chamelium->env);
2403 
2404 	for (i = 0; i < chamelium->port_count; i++)
2405 		free(chamelium->ports[i].name);
2406 
2407 	free(chamelium);
2408 }
2409 
2410 igt_constructor {
2411 	/* Frame dumps can be large, so we need to be able to handle very large
2412 	 * responses
2413 	 *
2414 	 * Limit here is 15MB
2415 	 */
2416 	xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 15728640);
2417 }
2418