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, ¶ms->clock);
815 xmlrpc_DECREF(val);
816 } else
817 params->clock = NAN;
818
819 read_int_from_xml_struct(chamelium, res, "htotal", ¶ms->htotal);
820 read_int_from_xml_struct(chamelium, res, "hactive", ¶ms->hactive);
821 read_int_from_xml_struct(chamelium, res, "hsync_offset",
822 ¶ms->hsync_offset);
823 read_int_from_xml_struct(chamelium, res, "hsync_width",
824 ¶ms->hsync_width);
825 read_int_from_xml_struct(chamelium, res, "hsync_polarity",
826 ¶ms->hsync_polarity);
827 read_int_from_xml_struct(chamelium, res, "vtotal", ¶ms->vtotal);
828 read_int_from_xml_struct(chamelium, res, "vactive", ¶ms->vactive);
829 read_int_from_xml_struct(chamelium, res, "vsync_offset",
830 ¶ms->vsync_offset);
831 read_int_from_xml_struct(chamelium, res, "vsync_width",
832 ¶ms->vsync_width);
833 read_int_from_xml_struct(chamelium, res, "vsync_polarity",
834 ¶ms->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