blob: b25855a41ceb054ca602e0904b9e4b19496be51e [file] [log] [blame]
Lyudec99f8b72016-10-18 14:12:09 -04001/*
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 <xmlrpc-c/base.h>
32#include <xmlrpc-c/client.h>
33#include <pthread.h>
34#include <glib.h>
35#include <pixman.h>
36#include <cairo.h>
37
Daniel Vetter8c1fcc62017-09-08 11:23:15 +020038#include "igt_chamelium.h"
39#include "igt_core.h"
40#include "igt_aux.h"
41#include "igt_kms.h"
42#include "igt_frame.h"
Petri Latvala08a2f882017-09-29 13:51:59 +030043#include "igt_rc.h"
Lyudec99f8b72016-10-18 14:12:09 -040044
45/**
46 * SECTION:igt_chamelium
47 * @short_description: Library for using the Chamelium into igt tests
48 * @title: Chamelium
49 * @include: igt_chamelium.h
50 *
51 * This library contains helpers for using Chameliums in IGT tests. This allows
52 * for tests to simulate more difficult tasks to automate such as display
53 * hotplugging, faulty display behaviors, etc.
54 *
55 * More information on the Chamelium can be found
56 * [on the ChromeOS project page](https://www.chromium.org/chromium-os/testing/chamelium).
57 *
58 * In order to run tests using the Chamelium, a valid configuration file must be
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +030059 * present. It must contain Chamelium-specific keys as shown with the following
60 * example:
Lyudec99f8b72016-10-18 14:12:09 -040061 *
62 * |[<!-- language="plain" -->
63 * [Chamelium]
64 * URL=http://chameleon:9992 # The URL used for connecting to the Chamelium's RPC server
65 *
66 * # The rest of the sections are used for defining connector mappings.
67 * # This is required so any tests using the Chamelium know which connector
68 * # on the test machine should be connected to each Chamelium port.
69 * #
70 * # In the event that any of these mappings are specified incorrectly,
71 * # any hotplugging tests for the incorrect connector mapping will fail.
72 *
73 * [Chamelium:DP-1] # The name of the DRM connector
74 * ChameliumPortID=1 # The ID of the port on the Chamelium this connector is attached to
75 *
76 * [Chamelium:HDMI-A-1]
77 * ChameliumPortID=3
78 * ]|
79 *
Lyudec99f8b72016-10-18 14:12:09 -040080 */
81
82struct chamelium_edid {
83 int id;
84 struct igt_list link;
85};
86
87struct chamelium_port {
88 unsigned int type;
89 int id;
90 int connector_id;
91 char *name;
92};
93
94struct chamelium_frame_dump {
95 unsigned char *bgr;
96 size_t size;
97 int width;
98 int height;
99 struct chamelium_port *port;
100};
101
Paul Kocialkowskie0802ba2017-07-19 16:46:05 +0300102struct chamelium_fb_crc_async_data {
103 cairo_surface_t *fb_surface;
104
105 pthread_t thread_id;
106 igt_crc_t *ret;
107};
108
Lyudec99f8b72016-10-18 14:12:09 -0400109struct chamelium {
110 xmlrpc_env env;
111 xmlrpc_client *client;
112 char *url;
113
114 /* Indicates the last port to have been used for capturing video */
115 struct chamelium_port *capturing_port;
116
117 int drm_fd;
118
119 struct chamelium_edid *edids;
120 struct chamelium_port *ports;
121 int port_count;
122};
123
124static struct chamelium *cleanup_instance;
125
126/**
127 * chamelium_get_ports:
128 * @chamelium: The Chamelium instance to use
129 * @count: Where to store the number of ports
130 *
131 * Retrieves all of the ports currently configured for use with this chamelium
132 *
133 * Returns: an array containing a pointer to each configured chamelium port
134 */
135struct chamelium_port **chamelium_get_ports(struct chamelium *chamelium,
136 int *count)
137{
138 int i;
139 struct chamelium_port **ret =
140 calloc(sizeof(void*), chamelium->port_count);
141
142 *count = chamelium->port_count;
143 for (i = 0; i < chamelium->port_count; i++)
144 ret[i] = &chamelium->ports[i];
145
146 return ret;
147}
148
149/**
150 * chamelium_port_get_type:
151 * @port: The chamelium port to retrieve the type from
152 *
153 * Retrieves the DRM connector type of the physical port on the Chamelium. It
154 * should be noted that this type may differ from the type provided by the
155 * driver.
156 *
157 * Returns: the DRM connector type of the physical Chamelium port
158 */
159unsigned int chamelium_port_get_type(const struct chamelium_port *port) {
160 return port->type;
161}
162
163/**
164 * chamelium_port_get_connector:
165 * @chamelium: The Chamelium instance to use
166 * @port: The chamelium port to retrieve the DRM connector for
167 * @reprobe: Whether or not to reprobe the DRM connector
168 *
169 * Get a drmModeConnector object for the given Chamelium port, and optionally
170 * reprobe the port in the process
171 *
172 * Returns: a drmModeConnector object corresponding to the given port
173 */
174drmModeConnector *chamelium_port_get_connector(struct chamelium *chamelium,
175 struct chamelium_port *port,
176 bool reprobe)
177{
178 drmModeConnector *connector;
179
180 if (reprobe)
181 connector = drmModeGetConnector(chamelium->drm_fd,
182 port->connector_id);
183 else
184 connector = drmModeGetConnectorCurrent(
185 chamelium->drm_fd, port->connector_id);
186
187 return connector;
188}
189
190/**
191 * chamelium_port_get_name:
192 * @port: The chamelium port to retrieve the name of
193 *
194 * Gets the name of the DRM connector corresponding to the given Chamelium
195 * port.
196 *
197 * Returns: the name of the DRM connector
198 */
199const char *chamelium_port_get_name(struct chamelium_port *port)
200{
201 return port->name;
202}
203
204/**
205 * chamelium_destroy_frame_dump:
206 * @dump: The frame dump to destroy
207 *
208 * Destroys the given frame dump and frees all of the resources associated with
209 * it.
210 */
211void chamelium_destroy_frame_dump(struct chamelium_frame_dump *dump)
212{
213 free(dump->bgr);
214 free(dump);
215}
216
217struct fsm_monitor_args {
218 struct chamelium *chamelium;
219 struct chamelium_port *port;
220 struct udev_monitor *mon;
221};
222
223/*
224 * Whenever resolutions or other factors change with the display output, the
225 * Chamelium's display receivers need to be fully reset in order to perform any
226 * frame-capturing related tasks. This requires cutting off the display then
227 * turning it back on, and is indicated by the Chamelium sending hotplug events
228 */
229static void *chamelium_fsm_mon(void *data)
230{
231 struct fsm_monitor_args *args = data;
232 drmModeConnector *connector;
233 int drm_fd = args->chamelium->drm_fd;
234
235 /*
236 * Wait for the chamelium to try unplugging the connector, otherwise
237 * the thread calling chamelium_rpc will kill us
238 */
239 igt_hotplug_detected(args->mon, 60);
240
241 /*
242 * Just in case the RPC call being executed returns before we complete
243 * the FSM modesetting sequence, so we don't leave the display in a bad
244 * state.
245 */
246 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
247
248 igt_debug("Chamelium needs FSM, handling\n");
249 connector = chamelium_port_get_connector(args->chamelium, args->port,
250 false);
251 kmstest_set_connector_dpms(drm_fd, connector, DRM_MODE_DPMS_OFF);
252 kmstest_set_connector_dpms(drm_fd, connector, DRM_MODE_DPMS_ON);
253
254 drmModeFreeConnector(connector);
255 return NULL;
256}
257
258static xmlrpc_value *chamelium_rpc(struct chamelium *chamelium,
259 struct chamelium_port *fsm_port,
260 const char *method_name,
261 const char *format_str,
262 ...)
263{
264 xmlrpc_value *res;
265 va_list va_args;
266 struct fsm_monitor_args monitor_args;
267 pthread_t fsm_thread_id;
268
269 /* Cleanup the last error, if any */
270 if (chamelium->env.fault_occurred) {
271 xmlrpc_env_clean(&chamelium->env);
272 xmlrpc_env_init(&chamelium->env);
273 }
274
275 /* Unfortunately xmlrpc_client's event loop helpers are rather useless
276 * for implementing any sort of event loop, since they provide no way
277 * to poll for events other then the RPC response. This means in order
278 * to handle the chamelium attempting FSM, we have to fork into another
279 * thread and have that handle hotplugging displays
280 */
281 if (fsm_port) {
282 monitor_args.chamelium = chamelium;
283 monitor_args.port = fsm_port;
284 monitor_args.mon = igt_watch_hotplug();
285 pthread_create(&fsm_thread_id, NULL, chamelium_fsm_mon,
286 &monitor_args);
287 }
288
289 va_start(va_args, format_str);
290 xmlrpc_client_call2f_va(&chamelium->env, chamelium->client,
291 chamelium->url, method_name, format_str, &res,
292 va_args);
293 va_end(va_args);
294
295 if (fsm_port) {
296 pthread_cancel(fsm_thread_id);
297 igt_cleanup_hotplug(monitor_args.mon);
298 }
299
300 igt_assert_f(!chamelium->env.fault_occurred,
301 "Chamelium RPC call failed: %s\n",
302 chamelium->env.fault_string);
303
304 return res;
305}
306
307/**
308 * chamelium_plug:
309 * @chamelium: The Chamelium instance to use
310 * @port: The port on the chamelium to plug
311 *
312 * Simulate a display connector being plugged into the system using the
313 * chamelium.
314 */
315void chamelium_plug(struct chamelium *chamelium, struct chamelium_port *port)
316{
317 igt_debug("Plugging %s\n", port->name);
318 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Plug", "(i)", port->id));
319}
320
321/**
322 * chamelium_unplug:
323 * @chamelium: The Chamelium instance to use
324 * @port: The port on the chamelium to unplug
325 *
326 * Simulate a display connector being unplugged from the system using the
327 * chamelium.
328 */
329void chamelium_unplug(struct chamelium *chamelium, struct chamelium_port *port)
330{
331 igt_debug("Unplugging port %s\n", port->name);
332 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Unplug", "(i)",
333 port->id));
334}
335
336/**
337 * chamelium_is_plugged:
338 * @chamelium: The Chamelium instance to use
339 * @port: The port on the Chamelium to check the status of
340 *
341 * Check whether or not the given port has been plugged into the system using
342 * #chamelium_plug.
343 *
344 * Returns: %true if the connector is set to plugged in, %false otherwise.
345 */
346bool chamelium_is_plugged(struct chamelium *chamelium,
347 struct chamelium_port *port)
348{
349 xmlrpc_value *res;
350 xmlrpc_bool is_plugged;
351
352 res = chamelium_rpc(chamelium, NULL, "IsPlugged", "(i)", port->id);
353
354 xmlrpc_read_bool(&chamelium->env, res, &is_plugged);
355 xmlrpc_DECREF(res);
356
357 return is_plugged;
358}
359
360/**
361 * chamelium_port_wait_video_input_stable:
362 * @chamelium: The Chamelium instance to use
363 * @port: The port on the Chamelium to check the status of
364 * @timeout_secs: How long to wait for a video signal to appear before timing
365 * out
366 *
367 * Waits for a video signal to appear on the given port. This is useful for
368 * checking whether or not we've setup a monitor correctly.
369 *
370 * Returns: %true if a video signal was detected, %false if we timed out
371 */
372bool chamelium_port_wait_video_input_stable(struct chamelium *chamelium,
373 struct chamelium_port *port,
374 int timeout_secs)
375{
376 xmlrpc_value *res;
377 xmlrpc_bool is_on;
378
379 igt_debug("Waiting for video input to stabalize on %s\n", port->name);
380
381 res = chamelium_rpc(chamelium, port, "WaitVideoInputStable", "(ii)",
382 port->id, timeout_secs);
383
384 xmlrpc_read_bool(&chamelium->env, res, &is_on);
385 xmlrpc_DECREF(res);
386
387 return is_on;
388}
389
390/**
391 * chamelium_fire_hpd_pulses:
392 * @chamelium: The Chamelium instance to use
393 * @port: The port to fire the HPD pulses on
394 * @width_msec: How long each pulse should last
395 * @count: The number of pulses to send
396 *
397 * A convienence function for sending multiple hotplug pulses to the system.
398 * The pulses start at low (e.g. connector is disconnected), and then alternate
399 * from high (e.g. connector is plugged in) to low. This is the equivalent of
400 * repeatedly calling #chamelium_plug and #chamelium_unplug, waiting
401 * @width_msec between each call.
402 *
403 * If @count is even, the last pulse sent will be high, and if it's odd then it
404 * will be low. Resetting the HPD line back to it's previous state, if desired,
405 * is the responsibility of the caller.
406 */
407void chamelium_fire_hpd_pulses(struct chamelium *chamelium,
408 struct chamelium_port *port,
409 int width_msec, int count)
410{
411 xmlrpc_value *pulse_widths = xmlrpc_array_new(&chamelium->env);
412 xmlrpc_value *width = xmlrpc_int_new(&chamelium->env, width_msec);
413 int i;
414
415 igt_debug("Firing %d HPD pulses with width of %d msec on %s\n",
416 count, width_msec, port->name);
417
418 for (i = 0; i < count; i++)
419 xmlrpc_array_append_item(&chamelium->env, pulse_widths, width);
420
421 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "FireMixedHpdPulses",
422 "(iA)", port->id, pulse_widths));
423
424 xmlrpc_DECREF(width);
425 xmlrpc_DECREF(pulse_widths);
426}
427
428/**
429 * chamelium_fire_mixed_hpd_pulses:
430 * @chamelium: The Chamelium instance to use
431 * @port: The port to fire the HPD pulses on
432 * @...: The length of each pulse in milliseconds, terminated with a %0
433 *
434 * Does the same thing as #chamelium_fire_hpd_pulses, but allows the caller to
435 * specify the length of each individual pulse.
436 */
437void chamelium_fire_mixed_hpd_pulses(struct chamelium *chamelium,
438 struct chamelium_port *port, ...)
439{
440 va_list args;
441 xmlrpc_value *pulse_widths = xmlrpc_array_new(&chamelium->env), *width;
442 int arg;
443
444 igt_debug("Firing mixed HPD pulses on %s\n", port->name);
445
446 va_start(args, port);
447 for (arg = va_arg(args, int); arg; arg = va_arg(args, int)) {
448 width = xmlrpc_int_new(&chamelium->env, arg);
449 xmlrpc_array_append_item(&chamelium->env, pulse_widths, width);
450 xmlrpc_DECREF(width);
451 }
452 va_end(args);
453
454 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "FireMixedHpdPulses",
455 "(iA)", port->id, pulse_widths));
456
457 xmlrpc_DECREF(pulse_widths);
458}
459
Lyudec99f8b72016-10-18 14:12:09 -0400460/**
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300461 * chamelium_schedule_hpd_toggle:
Lyudec99f8b72016-10-18 14:12:09 -0400462 * @chamelium: The Chamelium instance to use
463 * @port: The port to fire the HPD pulses on
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300464 * @delay_ms: Delay in milli-second before the toggle takes place
465 * @rising_edge: Whether the toggle should be a rising edge or a falling edge
Lyudec99f8b72016-10-18 14:12:09 -0400466 *
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300467 * Instructs the chamelium to schedule an hpd toggle (either a rising edge or
468 * a falling edge, depending on @rising_edg) after @delay_ms have passed.
469 * This is useful for testing things such as hpd after a suspend/resume cycle.
Lyudec99f8b72016-10-18 14:12:09 -0400470 */
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300471void chamelium_schedule_hpd_toggle(struct chamelium *chamelium,
472 struct chamelium_port *port, int delay_ms,
473 bool rising_edge)
Lyudec99f8b72016-10-18 14:12:09 -0400474{
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300475 igt_debug("Scheduling HPD toggle on %s in %d ms\n", port->name,
476 delay_ms);
Lyudec99f8b72016-10-18 14:12:09 -0400477
Paul Kocialkowkifa8b6ee2017-06-27 13:53:06 +0300478 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "ScheduleHpdToggle",
479 "(iii)", port->id, delay_ms, rising_edge));
Lyudec99f8b72016-10-18 14:12:09 -0400480}
481
482/**
483 * chamelium_new_edid:
484 * @chamelium: The Chamelium instance to use
485 * @edid: The edid blob to upload to the chamelium
486 *
487 * Uploads and registers a new EDID with the chamelium. The EDID will be
488 * destroyed automatically when #chamelium_deinit is called.
489 *
490 * Returns: The ID of the EDID uploaded to the chamelium.
491 */
492int chamelium_new_edid(struct chamelium *chamelium, const unsigned char *edid)
493{
494 xmlrpc_value *res;
495 struct chamelium_edid *allocated_edid;
496 int edid_id;
497
498 res = chamelium_rpc(chamelium, NULL, "CreateEdid", "(6)",
499 edid, EDID_LENGTH);
500
501 xmlrpc_read_int(&chamelium->env, res, &edid_id);
502 xmlrpc_DECREF(res);
503
504 allocated_edid = malloc(sizeof(struct chamelium_edid));
505 memset(allocated_edid, 0, sizeof(*allocated_edid));
506
507 allocated_edid->id = edid_id;
508 igt_list_init(&allocated_edid->link);
509
510 if (chamelium->edids)
511 igt_list_add(&chamelium->edids->link, &allocated_edid->link);
512 else
513 chamelium->edids = allocated_edid;
514
515 return edid_id;
516}
517
518static void chamelium_destroy_edid(struct chamelium *chamelium, int edid_id)
519{
520 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "DestroyEdid", "(i)",
521 edid_id));
522}
523
524/**
525 * chamelium_port_set_edid:
526 * @chamelium: The Chamelium instance to use
527 * @port: The port on the Chamelium to set the EDID on
528 * @edid_id: The ID of an EDID on the chamelium created with
529 * #chamelium_new_edid, or 0 to disable the EDID on the port
530 *
531 * Sets a port on the chamelium to use the specified EDID. This does not fire a
532 * hotplug pulse on it's own, and merely changes what EDID the chamelium port
533 * will report to us the next time we probe it. Users will need to reprobe the
534 * connectors themselves if they want to see the EDID reported by the port
535 * change.
536 */
537void chamelium_port_set_edid(struct chamelium *chamelium,
538 struct chamelium_port *port, int edid_id)
539{
540 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "ApplyEdid", "(ii)",
541 port->id, edid_id));
542}
543
544/**
545 * chamelium_port_set_ddc_state:
546 * @chamelium: The Chamelium instance to use
547 * @port: The port to change the DDC state on
548 * @enabled: Whether or not to enable the DDC bus
549 *
550 * This disables the DDC bus (e.g. the i2c line on the connector that gives us
551 * an EDID) of the specified port on the chamelium. This is useful for testing
552 * behavior on legacy connectors such as VGA, where the presence of a DDC bus
553 * is not always guaranteed.
554 */
555void chamelium_port_set_ddc_state(struct chamelium *chamelium,
556 struct chamelium_port *port,
557 bool enabled)
558{
559 igt_debug("%sabling DDC bus on %s\n",
560 enabled ? "En" : "Dis", port->name);
561
562 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "SetDdcState", "(ib)",
563 port->id, enabled));
564}
565
566/**
567 * chamelium_port_get_ddc_state:
568 * @chamelium: The Chamelium instance to use
569 * @port: The port on the Chamelium to check the status of
570 *
571 * Check whether or not the DDC bus on the specified chamelium port is enabled
572 * or not.
573 *
574 * Returns: %true if the DDC bus is enabled, %false otherwise.
575 */
576bool chamelium_port_get_ddc_state(struct chamelium *chamelium,
577 struct chamelium_port *port)
578{
579 xmlrpc_value *res;
580 xmlrpc_bool enabled;
581
582 res = chamelium_rpc(chamelium, NULL, "IsDdcEnabled", "(i)", port->id);
583 xmlrpc_read_bool(&chamelium->env, res, &enabled);
584
585 xmlrpc_DECREF(res);
586 return enabled;
587}
588
589/**
590 * chamelium_port_get_resolution:
591 * @chamelium: The Chamelium instance to use
592 * @port: The port on the Chamelium to check
593 * @x: Where to store the horizontal resolution of the port
594 * @y: Where to store the verical resolution of the port
595 *
596 * Check the current reported display resolution of the specified port on the
597 * chamelium. This information is provided by the chamelium itself, not DRM.
598 * Useful for verifying that we really are scanning out at the resolution we
599 * think we are.
600 */
601void chamelium_port_get_resolution(struct chamelium *chamelium,
602 struct chamelium_port *port,
603 int *x, int *y)
604{
605 xmlrpc_value *res, *res_x, *res_y;
606
607 res = chamelium_rpc(chamelium, port, "DetectResolution", "(i)",
608 port->id);
609
610 xmlrpc_array_read_item(&chamelium->env, res, 0, &res_x);
611 xmlrpc_array_read_item(&chamelium->env, res, 1, &res_y);
612 xmlrpc_read_int(&chamelium->env, res_x, x);
613 xmlrpc_read_int(&chamelium->env, res_y, y);
614
615 xmlrpc_DECREF(res_x);
616 xmlrpc_DECREF(res_y);
617 xmlrpc_DECREF(res);
618}
619
620static void chamelium_get_captured_resolution(struct chamelium *chamelium,
621 int *w, int *h)
622{
623 xmlrpc_value *res, *res_w, *res_h;
624
625 res = chamelium_rpc(chamelium, NULL, "GetCapturedResolution", "()");
626
627 xmlrpc_array_read_item(&chamelium->env, res, 0, &res_w);
628 xmlrpc_array_read_item(&chamelium->env, res, 1, &res_h);
629 xmlrpc_read_int(&chamelium->env, res_w, w);
630 xmlrpc_read_int(&chamelium->env, res_h, h);
631
632 xmlrpc_DECREF(res_w);
633 xmlrpc_DECREF(res_h);
634 xmlrpc_DECREF(res);
635}
636
637static struct chamelium_frame_dump *frame_from_xml(struct chamelium *chamelium,
638 xmlrpc_value *frame_xml)
639{
640 struct chamelium_frame_dump *ret = malloc(sizeof(*ret));
641
642 chamelium_get_captured_resolution(chamelium, &ret->width, &ret->height);
643 ret->port = chamelium->capturing_port;
644 xmlrpc_read_base64(&chamelium->env, frame_xml, &ret->size,
645 (void*)&ret->bgr);
646
647 return ret;
648}
649
650/**
651 * chamelium_port_dump_pixels:
652 * @chamelium: The Chamelium instance to use
653 * @port: The port to perform the video capture on
654 * @x: The X coordinate to crop the screen capture to
655 * @y: The Y coordinate to crop the screen capture to
656 * @w: The width of the area to crop the screen capture to, or 0 for the whole
657 * screen
658 * @h: The height of the area to crop the screen capture to, or 0 for the whole
659 * screen
660 *
661 * Captures the currently displayed image on the given chamelium port,
662 * optionally cropped to a given region. In situations where pre-calculating
663 * CRCs may not be reliable, this can be used as an alternative for figuring
664 * out whether or not the correct images are being displayed on the screen.
665 *
666 * The frame dump data returned by this function should be freed when the
667 * caller is done with it using #chamelium_destroy_frame_dump.
668 *
669 * As an important note: some of the EDIDs provided by the Chamelium cause
670 * certain GPU drivers to default to using limited color ranges. This can cause
671 * video captures from the Chamelium to provide different images then expected
672 * due to the difference in color ranges (framebuffer uses full color range,
673 * but the video output doesn't), and as a result lead to CRC mismatches. To
674 * workaround this, the caller should force the connector to use full color
675 * ranges by using #kmstest_set_connector_broadcast_rgb before setting up the
676 * display.
677 *
678 * Returns: a chamelium_frame_dump struct
679 */
680struct chamelium_frame_dump *chamelium_port_dump_pixels(struct chamelium *chamelium,
681 struct chamelium_port *port,
682 int x, int y,
683 int w, int h)
684{
685 xmlrpc_value *res;
686 struct chamelium_frame_dump *frame;
687
688 res = chamelium_rpc(chamelium, port, "DumpPixels",
689 (w && h) ? "(iiiii)" : "(innnn)",
690 port->id, x, y, w, h);
691 chamelium->capturing_port = port;
692
693 frame = frame_from_xml(chamelium, res);
694 xmlrpc_DECREF(res);
695
696 return frame;
697}
698
699static void crc_from_xml(struct chamelium *chamelium,
700 xmlrpc_value *xml_crc, igt_crc_t *out)
701{
702 xmlrpc_value *res;
703 int i;
704
705 out->n_words = xmlrpc_array_size(&chamelium->env, xml_crc);
706 for (i = 0; i < out->n_words; i++) {
707 xmlrpc_array_read_item(&chamelium->env, xml_crc, i, &res);
708 xmlrpc_read_int(&chamelium->env, res, (int*)&out->crc[i]);
709 xmlrpc_DECREF(res);
710 }
711}
712
713/**
714 * chamelium_get_crc_for_area:
715 * @chamelium: The Chamelium instance to use
716 * @port: The port to perform the CRC checking on
717 * @x: The X coordinate on the emulated display to start calculating the CRC
718 * from
719 * @y: The Y coordinate on the emulated display to start calculating the CRC
720 * from
721 * @w: The width of the area to fetch the CRC from, or %0 for the whole display
722 * @h: The height of the area to fetch the CRC from, or %0 for the whole display
723 *
724 * Reads back the pixel CRC for an area on the specified chamelium port. This
725 * is the same as using the CRC readback from a GPU, the main difference being
726 * the data is provided by the chamelium and also allows us to specify a region
727 * of the screen to use as opposed to the entire thing.
728 *
729 * As an important note: some of the EDIDs provided by the Chamelium cause
730 * certain GPU drivers to default to using limited color ranges. This can cause
731 * video captures from the Chamelium to provide different images then expected
732 * due to the difference in color ranges (framebuffer uses full color range,
733 * but the video output doesn't), and as a result lead to CRC mismatches. To
734 * workaround this, the caller should force the connector to use full color
735 * ranges by using #kmstest_set_connector_broadcast_rgb before setting up the
736 * display.
737 *
738 * After the caller is finished with the EDID returned by this function, the
739 * caller should manually free the resources associated with it.
740 *
741 * Returns: The CRC read back from the chamelium
742 */
743igt_crc_t *chamelium_get_crc_for_area(struct chamelium *chamelium,
744 struct chamelium_port *port,
745 int x, int y, int w, int h)
746{
747 xmlrpc_value *res;
748 igt_crc_t *ret = malloc(sizeof(igt_crc_t));
749
750 res = chamelium_rpc(chamelium, port, "ComputePixelChecksum",
751 (w && h) ? "(iiiii)" : "(innnn)",
752 port->id, x, y, w, h);
753 chamelium->capturing_port = port;
754
755 crc_from_xml(chamelium, res, ret);
756 xmlrpc_DECREF(res);
757
758 return ret;
759}
760
761/**
762 * chamelium_start_capture:
763 * @chamelium: The Chamelium instance to use
764 * @port: The port to perform the video capture on
765 * @x: The X coordinate to crop the video to
766 * @y: The Y coordinate to crop the video to
767 * @w: The width of the cropped video, or %0 for the whole display
768 * @h: The height of the cropped video, or %0 for the whole display
769 *
770 * Starts capturing video frames on the given Chamelium port. Once the user is
771 * finished capturing frames, they should call #chamelium_stop_capture.
772 *
773 * A blocking, one-shot version of this function is available: see
774 * #chamelium_capture
775 *
776 * As an important note: some of the EDIDs provided by the Chamelium cause
777 * certain GPU drivers to default to using limited color ranges. This can cause
778 * video captures from the Chamelium to provide different images then expected
779 * due to the difference in color ranges (framebuffer uses full color range,
780 * but the video output doesn't), and as a result lead to CRC and frame dump
781 * comparison mismatches. To workaround this, the caller should force the
782 * connector to use full color ranges by using
783 * #kmstest_set_connector_broadcast_rgb before setting up the display.
784 */
785void chamelium_start_capture(struct chamelium *chamelium,
786 struct chamelium_port *port, int x, int y, int w, int h)
787{
788 xmlrpc_DECREF(chamelium_rpc(chamelium, port, "StartCapturingVideo",
789 (w && h) ? "(iiiii)" : "(innnn)",
790 port->id, x, y, w, h));
791 chamelium->capturing_port = port;
792}
793
794/**
795 * chamelium_stop_capture:
796 * @chamelium: The Chamelium instance to use
797 * @frame_count: The number of frames to wait to capture, or %0 to stop
798 * immediately
799 *
800 * Finishes capturing video frames on the given Chamelium port. If @frame_count
801 * is specified, this call will block until the given number of frames have been
802 * captured.
803 */
804void chamelium_stop_capture(struct chamelium *chamelium, int frame_count)
805{
806 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "StopCapturingVideo",
807 "(i)", frame_count));
808}
809
810/**
811 * chamelium_capture:
812 * @chamelium: The Chamelium instance to use
813 * @port: The port to perform the video capture on
814 * @x: The X coordinate to crop the video to
815 * @y: The Y coordinate to crop the video to
816 * @w: The width of the cropped video, or %0 for the whole display
817 * @h: The height of the cropped video, or %0 for the whole display
818 * @frame_count: The number of frames to capture
819 *
820 * Captures the given number of frames on the chamelium. This is equivalent to
821 * calling #chamelium_start_capture immediately followed by
822 * #chamelium_stop_capture. The caller is blocked until all of the frames have
823 * been captured.
824 *
825 * As an important note: some of the EDIDs provided by the Chamelium cause
826 * certain GPU drivers to default to using limited color ranges. This can cause
827 * video captures from the Chamelium to provide different images then expected
828 * due to the difference in color ranges (framebuffer uses full color range,
829 * but the video output doesn't), and as a result lead to CRC and frame dump
830 * comparison mismatches. To workaround this, the caller should force the
831 * connector to use full color ranges by using
832 * #kmstest_set_connector_broadcast_rgb before setting up the display.
833 */
834void chamelium_capture(struct chamelium *chamelium, struct chamelium_port *port,
835 int x, int y, int w, int h, int frame_count)
836{
837 xmlrpc_DECREF(chamelium_rpc(chamelium, port, "CaptureVideo",
838 (w && h) ? "(iiiiii)" : "(iinnnn)",
839 port->id, frame_count, x, y, w, h));
840 chamelium->capturing_port = port;
841}
842
843/**
844 * chamelium_read_captured_crcs:
845 * @chamelium: The Chamelium instance to use
846 * @frame_count: Where to store the number of CRCs we read in
847 *
848 * Reads all of the CRCs that have been captured thus far from the Chamelium.
849 *
850 * Returns: An array of @frame_count length containing all of the CRCs we read
851 */
852igt_crc_t *chamelium_read_captured_crcs(struct chamelium *chamelium,
853 int *frame_count)
854{
855 igt_crc_t *ret;
856 xmlrpc_value *res, *elem;
857 int i;
858
859 res = chamelium_rpc(chamelium, NULL, "GetCapturedChecksums", "(in)", 0);
860
861 *frame_count = xmlrpc_array_size(&chamelium->env, res);
862 ret = calloc(sizeof(igt_crc_t), *frame_count);
863
864 for (i = 0; i < *frame_count; i++) {
865 xmlrpc_array_read_item(&chamelium->env, res, i, &elem);
866
867 crc_from_xml(chamelium, elem, &ret[i]);
868 ret[i].frame = i;
869
870 xmlrpc_DECREF(elem);
871 }
872
873 xmlrpc_DECREF(res);
874
875 return ret;
876}
877
878/**
879 * chamelium_port_read_captured_frame:
880 *
881 * @chamelium: The Chamelium instance to use
882 * @index: The index of the captured frame we want to get
883 *
884 * Retrieves a single video frame captured during the last video capture on the
885 * Chamelium. This data should be freed using #chamelium_destroy_frame_data
886 *
887 * Returns: a chamelium_frame_dump struct
888 */
889struct chamelium_frame_dump *chamelium_read_captured_frame(struct chamelium *chamelium,
890 unsigned int index)
891{
892 xmlrpc_value *res;
893 struct chamelium_frame_dump *frame;
894
895 res = chamelium_rpc(chamelium, NULL, "ReadCapturedFrame", "(i)", index);
896 frame = frame_from_xml(chamelium, res);
897 xmlrpc_DECREF(res);
898
899 return frame;
900}
901
902/**
903 * chamelium_get_captured_frame_count:
904 * @chamelium: The Chamelium instance to use
905 *
906 * Gets the number of frames that were captured during the last video capture.
907 *
908 * Returns: the number of frames the Chamelium captured during the last video
909 * capture.
910 */
911int chamelium_get_captured_frame_count(struct chamelium *chamelium)
912{
913 xmlrpc_value *res;
914 int ret;
915
916 res = chamelium_rpc(chamelium, NULL, "GetCapturedFrameCount", "()");
917 xmlrpc_read_int(&chamelium->env, res, &ret);
918
919 xmlrpc_DECREF(res);
920 return ret;
921}
922
923static pixman_image_t *convert_frame_format(pixman_image_t *src,
924 int format)
925{
926 pixman_image_t *converted;
927 int w = pixman_image_get_width(src), h = pixman_image_get_height(src);
928
929 converted = pixman_image_create_bits(format, w, h, NULL,
Paul Kocialkowskidb54f982017-06-16 17:19:02 +0300930 PIXMAN_FORMAT_BPP(format) / 8 * w);
Lyudec99f8b72016-10-18 14:12:09 -0400931 pixman_image_composite(PIXMAN_OP_ADD, src, NULL, converted,
932 0, 0, 0, 0, 0, 0, w, h);
933
934 return converted;
935}
936
Paul Kocialkowski5e4e8292017-07-19 16:46:09 +0300937static cairo_surface_t *convert_frame_dump_argb32(const struct chamelium_frame_dump *dump)
938{
939 cairo_surface_t *dump_surface;
940 pixman_image_t *image_bgr;
941 pixman_image_t *image_argb;
942 int w = dump->width, h = dump->height;
943 uint32_t *bits_bgr = (uint32_t *) dump->bgr;
944 unsigned char *bits_argb;
Paul Kocialkowskie246ff02017-07-20 18:13:37 +0300945 unsigned char *bits_target;
946 int size;
Paul Kocialkowski5e4e8292017-07-19 16:46:09 +0300947
948 image_bgr = pixman_image_create_bits(
949 PIXMAN_b8g8r8, w, h, bits_bgr,
950 PIXMAN_FORMAT_BPP(PIXMAN_b8g8r8) / 8 * w);
951 image_argb = convert_frame_format(image_bgr, PIXMAN_x8r8g8b8);
952 pixman_image_unref(image_bgr);
953
954 bits_argb = (unsigned char *) pixman_image_get_data(image_argb);
955
Paul Kocialkowskie246ff02017-07-20 18:13:37 +0300956 dump_surface = cairo_image_surface_create(
957 CAIRO_FORMAT_ARGB32, w, h);
958
959 bits_target = cairo_image_surface_get_data(dump_surface);
960 size = cairo_image_surface_get_stride(dump_surface) * h;
961 memcpy(bits_target, bits_argb, size);
962 cairo_surface_mark_dirty(dump_surface);
Paul Kocialkowski5e4e8292017-07-19 16:46:09 +0300963
964 pixman_image_unref(image_argb);
965
966 return dump_surface;
967}
968
Lyudec99f8b72016-10-18 14:12:09 -0400969/**
970 * chamelium_assert_frame_eq:
971 * @chamelium: The chamelium instance the frame dump belongs to
972 * @dump: The chamelium frame dump to check
973 * @fb: The framebuffer to check against
974 *
975 * Asserts that the image contained in the chamelium frame dump is identical to
976 * the given framebuffer. Useful for scenarios where pre-calculating CRCs might
977 * not be ideal.
978 */
979void chamelium_assert_frame_eq(const struct chamelium *chamelium,
980 const struct chamelium_frame_dump *dump,
981 struct igt_fb *fb)
982{
Lyudec99f8b72016-10-18 14:12:09 -0400983 cairo_surface_t *fb_surface;
984 pixman_image_t *reference_src, *reference_bgr;
985 int w = dump->width, h = dump->height;
986 bool eq;
987
988 /* Get the cairo surface for the framebuffer */
Paul Kocialkowskib4ad3972017-07-19 16:46:04 +0300989 fb_surface = igt_get_cairo_surface(chamelium->drm_fd, fb);
Lyudec99f8b72016-10-18 14:12:09 -0400990
991 /*
992 * Convert the reference image into the same format as the chamelium
993 * image
994 */
995 reference_src = pixman_image_create_bits(
996 PIXMAN_x8r8g8b8, w, h,
997 (void*)cairo_image_surface_get_data(fb_surface),
998 cairo_image_surface_get_stride(fb_surface));
999 reference_bgr = convert_frame_format(reference_src, PIXMAN_b8g8r8);
1000 pixman_image_unref(reference_src);
1001
1002 /* Now do the actual comparison */
1003 eq = memcmp(dump->bgr, pixman_image_get_data(reference_bgr),
1004 dump->size) == 0;
1005
1006 pixman_image_unref(reference_bgr);
Lyudec99f8b72016-10-18 14:12:09 -04001007
1008 igt_fail_on_f(!eq,
1009 "Chamelium frame dump didn't match reference image\n");
1010}
1011
1012/**
Paul Kocialkowski5e4e8292017-07-19 16:46:09 +03001013 * chamelium_assert_crc_eq_or_dump:
1014 * @chamelium: The chamelium instance the frame dump belongs to
1015 * @reference_crc: The CRC for the reference frame
1016 * @capture_crc: The CRC for the captured frame
1017 * @fb: pointer to an #igt_fb structure
1018 *
1019 * Asserts that the CRC provided for both the reference and the captured frame
1020 * are identical. If they are not, this grabs the captured frame and saves it
1021 * along with the reference to a png file.
1022 */
1023void chamelium_assert_crc_eq_or_dump(struct chamelium *chamelium,
1024 igt_crc_t *reference_crc,
1025 igt_crc_t *capture_crc, struct igt_fb *fb,
1026 int index)
1027{
1028 struct chamelium_frame_dump *frame;
1029 cairo_surface_t *reference;
1030 cairo_surface_t *capture;
1031 char *reference_suffix;
1032 char *capture_suffix;
1033 bool eq;
1034
1035 eq = igt_check_crc_equal(reference_crc, capture_crc);
1036 if (!eq && igt_frame_dump_is_enabled()) {
1037 /* Grab the reference frame from framebuffer */
1038 reference = igt_get_cairo_surface(chamelium->drm_fd, fb);
1039
1040 /* Grab the captured frame from chamelium */
1041 frame = chamelium_read_captured_frame(chamelium, index);
1042 igt_assert(frame);
1043
1044 capture = convert_frame_dump_argb32(frame);
1045
1046 reference_suffix = igt_crc_to_string_extended(reference_crc,
1047 '-', 2);
1048 capture_suffix = igt_crc_to_string_extended(capture_crc, '-',
1049 2);
1050
1051 /* Write reference and capture frames to png */
1052 igt_write_compared_frames_to_png(reference, capture,
1053 reference_suffix,
1054 capture_suffix);
1055
1056 free(reference_suffix);
1057 free(capture_suffix);
1058
1059 chamelium_destroy_frame_dump(frame);
1060
1061 cairo_surface_destroy(capture);
1062 }
1063
1064 igt_assert(eq);
1065}
1066
1067/**
Paul Kocialkowskie246ff02017-07-20 18:13:37 +03001068 * chamelium_assert_analog_frame_match_or_dump:
1069 * @chamelium: The chamelium instance the frame dump belongs to
1070 * @frame: The chamelium frame dump to match
1071 * @fb: pointer to an #igt_fb structure
1072 *
1073 * Asserts that the provided captured frame matches the reference frame from
1074 * the framebuffer. If they do not, this saves the reference and captured frames
1075 * to a png file.
1076 */
1077void chamelium_assert_analog_frame_match_or_dump(struct chamelium *chamelium,
1078 struct chamelium_port *port,
1079 const struct chamelium_frame_dump *frame,
1080 struct igt_fb *fb)
1081{
1082 cairo_surface_t *reference;
1083 cairo_surface_t *capture;
1084 igt_crc_t *reference_crc;
1085 igt_crc_t *capture_crc;
1086 char *reference_suffix;
1087 char *capture_suffix;
1088 bool match;
1089
1090 /* Grab the reference frame from framebuffer */
1091 reference = igt_get_cairo_surface(chamelium->drm_fd, fb);
1092
1093 /* Grab the captured frame from chamelium */
1094 capture = convert_frame_dump_argb32(frame);
1095
1096 match = igt_check_analog_frame_match(reference, capture);
1097 if (!match && igt_frame_dump_is_enabled()) {
1098 reference_crc = chamelium_calculate_fb_crc(chamelium->drm_fd,
1099 fb);
1100 capture_crc = chamelium_get_crc_for_area(chamelium, port, 0, 0,
1101 0, 0);
1102
1103 reference_suffix = igt_crc_to_string_extended(reference_crc,
1104 '-', 2);
1105 capture_suffix = igt_crc_to_string_extended(capture_crc, '-',
1106 2);
1107
1108 /* Write reference and capture frames to png */
1109 igt_write_compared_frames_to_png(reference, capture,
1110 reference_suffix,
1111 capture_suffix);
1112
1113 free(reference_suffix);
1114 free(capture_suffix);
1115 }
1116
1117 cairo_surface_destroy(capture);
1118
1119 igt_assert(match);
1120}
1121
1122
1123/**
1124 * chamelium_analog_frame_crop:
1125 * @chamelium: The Chamelium instance to use
1126 * @dump: The chamelium frame dump to crop
1127 * @width: The cropped frame width
1128 * @height: The cropped frame height
1129 *
1130 * Detects the corners of a chamelium frame and crops it to the requested
1131 * width/height. This is useful for VGA frame dumps that also contain the
1132 * pixels dumped during the blanking intervals.
1133 *
1134 * The detection is done on a brightness-threshold-basis, that is adapted
1135 * to the reference frame used by i-g-t. It may not be as relevant for other
1136 * frames.
1137 */
1138void chamelium_crop_analog_frame(struct chamelium_frame_dump *dump, int width,
1139 int height)
1140{
1141 unsigned char *bgr;
1142 unsigned char *p;
1143 unsigned char *q;
1144 int top, left;
1145 int x, y, xx, yy;
1146 int score;
1147
1148 if (dump->width == width && dump->height == height)
1149 return;
1150
1151 /* Start with the most bottom-right position. */
1152 top = dump->height - height;
1153 left = dump->width - width;
1154
1155 igt_assert(top >= 0 && left >= 0);
1156
1157 igt_debug("Cropping analog frame from %dx%d to %dx%d\n", dump->width,
1158 dump->height, width, height);
1159
1160 /* Detect the top-left corner of the frame. */
1161 for (x = 0; x < dump->width; x++) {
1162 for (y = 0; y < dump->height; y++) {
1163 p = &dump->bgr[(x + y * dump->width) * 3];
1164
1165 /* Detect significantly bright pixels. */
1166 if (p[0] < 50 && p[1] < 50 && p[2] < 50)
1167 continue;
1168
1169 /*
1170 * Make sure close-by pixels are also significantly
1171 * bright.
1172 */
1173 score = 0;
1174 for (xx = x; xx < x + 10; xx++) {
1175 for (yy = y; yy < y + 10; yy++) {
1176 p = &dump->bgr[(xx + yy * dump->width) * 3];
1177
1178 if (p[0] > 50 && p[1] > 50 && p[2] > 50)
1179 score++;
1180 }
1181 }
1182
1183 /* Not enough pixels are significantly bright. */
1184 if (score < 25)
1185 continue;
1186
1187 if (x < left)
1188 left = x;
1189
1190 if (y < top)
1191 top = y;
1192
1193 if (left == x || top == y)
1194 continue;
1195 }
1196 }
1197
1198 igt_debug("Detected analog frame edges at %dx%d\n", left, top);
1199
1200 /* Crop the frame given the detected top-left corner. */
1201 bgr = malloc(width * height * 3);
1202
1203 for (y = 0; y < height; y++) {
1204 p = &dump->bgr[(left + (top + y) * dump->width) * 3];
1205 q = &bgr[(y * width) * 3];
1206 memcpy(q, p, width * 3);
1207 }
1208
1209 free(dump->bgr);
1210 dump->width = width;
1211 dump->height = height;
1212 dump->bgr = bgr;
1213}
1214
1215/**
Lyudec99f8b72016-10-18 14:12:09 -04001216 * chamelium_get_frame_limit:
1217 * @chamelium: The Chamelium instance to use
1218 * @port: The port to check the frame limit on
1219 * @w: The width of the area to get the capture frame limit for, or %0 for the
1220 * whole display
1221 * @h: The height of the area to get the capture frame limit for, or %0 for the
1222 * whole display
1223 *
1224 * Gets the max number of frames we can capture with the Chamelium for the given
1225 * resolution.
1226 *
1227 * Returns: The number of the max number of frames we can capture
1228 */
1229int chamelium_get_frame_limit(struct chamelium *chamelium,
1230 struct chamelium_port *port,
1231 int w, int h)
1232{
1233 xmlrpc_value *res;
1234 int ret;
1235
1236 if (!w && !h)
1237 chamelium_port_get_resolution(chamelium, port, &w, &h);
1238
1239 res = chamelium_rpc(chamelium, port, "GetMaxFrameLimit", "(iii)",
1240 port->id, w, h);
1241
1242 xmlrpc_read_int(&chamelium->env, res, &ret);
1243 xmlrpc_DECREF(res);
1244
1245 return ret;
1246}
1247
Paul Kocialkowskie0802ba2017-07-19 16:46:05 +03001248static uint32_t chamelium_xrgb_hash16(const unsigned char *buffer, int width,
1249 int height, int k, int m)
1250{
1251 unsigned char r, g, b;
1252 uint64_t sum = 0;
1253 uint64_t count = 0;
1254 uint64_t value;
1255 uint32_t hash;
1256 int index;
1257 int i;
1258
1259 for (i=0; i < width * height; i++) {
1260 if ((i % m) != k)
1261 continue;
1262
1263 index = i * 4;
1264
1265 r = buffer[index + 2];
1266 g = buffer[index + 1];
1267 b = buffer[index + 0];
1268
1269 value = r | (g << 8) | (b << 16);
1270 sum += ++count * value;
1271 }
1272
1273 hash = ((sum >> 0) ^ (sum >> 16) ^ (sum >> 32) ^ (sum >> 48)) & 0xffff;
1274
1275 return hash;
1276}
1277
1278static void chamelium_do_calculate_fb_crc(cairo_surface_t *fb_surface,
1279 igt_crc_t *out)
1280{
1281 unsigned char *buffer;
1282 int n = 4;
1283 int w, h;
1284 int i, j;
1285
1286 buffer = cairo_image_surface_get_data(fb_surface);
1287 w = cairo_image_surface_get_width(fb_surface);
1288 h = cairo_image_surface_get_height(fb_surface);
1289
1290 for (i = 0; i < n; i++) {
1291 j = n - i - 1;
1292 out->crc[i] = chamelium_xrgb_hash16(buffer, w, h, j, n);
1293 }
1294
1295 out->n_words = n;
1296}
1297
1298/**
1299 * chamelium_calculate_fb_crc:
1300 * @fd: The drm file descriptor
1301 * @fb: The framebuffer to calculate the CRC for
1302 *
1303 * Calculates the CRC for the provided framebuffer, using the Chamelium's CRC
1304 * algorithm. This calculates the CRC in a synchronous fashion.
1305 *
1306 * Returns: The calculated CRC
1307 */
1308igt_crc_t *chamelium_calculate_fb_crc(int fd, struct igt_fb *fb)
1309{
1310 igt_crc_t *ret = calloc(1, sizeof(igt_crc_t));
1311 cairo_surface_t *fb_surface;
1312
1313 /* Get the cairo surface for the framebuffer */
1314 fb_surface = igt_get_cairo_surface(fd, fb);
1315
1316 chamelium_do_calculate_fb_crc(fb_surface, ret);
1317
1318 return ret;
1319}
1320
1321static void *chamelium_calculate_fb_crc_async_work(void *data)
1322{
1323 struct chamelium_fb_crc_async_data *fb_crc;
1324
1325 fb_crc = (struct chamelium_fb_crc_async_data *) data;
1326
1327 chamelium_do_calculate_fb_crc(fb_crc->fb_surface, fb_crc->ret);
1328
1329 return NULL;
1330}
1331
1332/**
1333 * chamelium_calculate_fb_crc_launch:
1334 * @fd: The drm file descriptor
1335 * @fb: The framebuffer to calculate the CRC for
1336 *
1337 * Launches the CRC calculation for the provided framebuffer, using the
1338 * Chamelium's CRC algorithm. This calculates the CRC in an asynchronous
1339 * fashion.
1340 *
1341 * The returned structure should be passed to a subsequent call to
1342 * chamelium_calculate_fb_crc_result. It should not be freed.
1343 *
1344 * Returns: An intermediate structure for the CRC calculation work.
1345 */
1346struct chamelium_fb_crc_async_data *chamelium_calculate_fb_crc_async_start(int fd,
1347 struct igt_fb *fb)
1348{
1349 struct chamelium_fb_crc_async_data *fb_crc;
1350
1351 fb_crc = calloc(1, sizeof(struct chamelium_fb_crc_async_data));
1352 fb_crc->ret = calloc(1, sizeof(igt_crc_t));
1353
1354 /* Get the cairo surface for the framebuffer */
1355 fb_crc->fb_surface = igt_get_cairo_surface(fd, fb);
1356
1357 pthread_create(&fb_crc->thread_id, NULL,
1358 chamelium_calculate_fb_crc_async_work, fb_crc);
1359
1360 return fb_crc;
1361}
1362
1363/**
1364 * chamelium_calculate_fb_crc_result:
1365 * @fb_crc: An intermediate structure with thread-related information
1366 *
1367 * Blocks until the asynchronous CRC calculation is finished, and then returns
1368 * its result.
1369 *
1370 * Returns: The calculated CRC
1371 */
1372igt_crc_t *chamelium_calculate_fb_crc_async_finish(struct chamelium_fb_crc_async_data *fb_crc)
1373{
1374 igt_crc_t *ret;
1375
1376 pthread_join(fb_crc->thread_id, NULL);
1377
1378 ret = fb_crc->ret;
1379 free(fb_crc);
1380
1381 return ret;
1382}
1383
Lyudec99f8b72016-10-18 14:12:09 -04001384static unsigned int chamelium_get_port_type(struct chamelium *chamelium,
1385 struct chamelium_port *port)
1386{
1387 xmlrpc_value *res;
1388 const char *port_type_str;
1389 unsigned int port_type;
1390
1391 res = chamelium_rpc(chamelium, NULL, "GetConnectorType",
1392 "(i)", port->id);
1393
1394 xmlrpc_read_string(&chamelium->env, res, &port_type_str);
1395 igt_debug("Port %d is of type '%s'\n", port->id, port_type_str);
1396
1397 if (strcmp(port_type_str, "DP") == 0)
1398 port_type = DRM_MODE_CONNECTOR_DisplayPort;
1399 else if (strcmp(port_type_str, "HDMI") == 0)
1400 port_type = DRM_MODE_CONNECTOR_HDMIA;
1401 else if (strcmp(port_type_str, "VGA") == 0)
1402 port_type = DRM_MODE_CONNECTOR_VGA;
1403 else
1404 port_type = DRM_MODE_CONNECTOR_Unknown;
1405
1406 free((void*)port_type_str);
1407 xmlrpc_DECREF(res);
1408
1409 return port_type;
1410}
1411
1412static bool chamelium_read_port_mappings(struct chamelium *chamelium,
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001413 int drm_fd)
Lyudec99f8b72016-10-18 14:12:09 -04001414{
1415 drmModeRes *res;
1416 drmModeConnector *connector;
1417 struct chamelium_port *port;
1418 GError *error = NULL;
1419 char **group_list;
1420 char *group, *map_name;
1421 int port_i, i, j;
1422 bool ret = true;
1423
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001424 group_list = g_key_file_get_groups(igt_key_file, NULL);
Lyudec99f8b72016-10-18 14:12:09 -04001425
1426 /* Count how many connector mappings are specified in the config */
1427 for (i = 0; group_list[i] != NULL; i++) {
1428 if (strstr(group_list[i], "Chamelium:"))
1429 chamelium->port_count++;
1430 }
1431
1432 chamelium->ports = calloc(sizeof(struct chamelium_port),
1433 chamelium->port_count);
1434 port_i = 0;
1435 res = drmModeGetResources(drm_fd);
1436
1437 for (i = 0; group_list[i] != NULL; i++) {
1438 group = group_list[i];
1439
1440 if (!strstr(group, "Chamelium:"))
1441 continue;
1442
1443 map_name = group + (sizeof("Chamelium:") - 1);
1444
1445 port = &chamelium->ports[port_i++];
1446 port->name = strdup(map_name);
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001447 port->id = g_key_file_get_integer(igt_key_file, group,
Lyudec99f8b72016-10-18 14:12:09 -04001448 "ChameliumPortID",
1449 &error);
1450 if (!port->id) {
1451 igt_warn("Failed to read chamelium port ID for %s: %s\n",
1452 map_name, error->message);
1453 ret = false;
1454 goto out;
1455 }
1456
1457 port->type = chamelium_get_port_type(chamelium, port);
1458 if (port->type == DRM_MODE_CONNECTOR_Unknown) {
1459 igt_warn("Unable to retrieve the physical port type from the Chamelium for '%s'\n",
1460 map_name);
1461 ret = false;
1462 goto out;
1463 }
1464
1465 for (j = 0;
1466 j < res->count_connectors && !port->connector_id;
1467 j++) {
1468 char name[50];
1469
1470 connector = drmModeGetConnectorCurrent(
1471 drm_fd, res->connectors[j]);
1472
1473 /* We have to generate the connector name on our own */
1474 snprintf(name, 50, "%s-%u",
1475 kmstest_connector_type_str(connector->connector_type),
1476 connector->connector_type_id);
1477
1478 if (strcmp(name, map_name) == 0)
1479 port->connector_id = connector->connector_id;
1480
1481 drmModeFreeConnector(connector);
1482 }
1483 if (!port->connector_id) {
1484 igt_warn("No connector found with name '%s'\n",
1485 map_name);
1486 ret = false;
1487 goto out;
1488 }
1489
1490 igt_debug("Port '%s' with physical type '%s' mapped to Chamelium port %d\n",
1491 map_name, kmstest_connector_type_str(port->type),
1492 port->id);
1493 }
1494
1495out:
1496 drmModeFreeResources(res);
1497 g_strfreev(group_list);
1498
1499 return ret;
1500}
1501
1502static bool chamelium_read_config(struct chamelium *chamelium, int drm_fd)
1503{
Lyudec99f8b72016-10-18 14:12:09 -04001504 GError *error = NULL;
Lyudec99f8b72016-10-18 14:12:09 -04001505
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001506 if (!igt_key_file) {
1507 igt_warn("No configuration file available for chamelium\n");
1508 return false;
Lyudec99f8b72016-10-18 14:12:09 -04001509 }
1510
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001511 chamelium->url = g_key_file_get_string(igt_key_file, "Chamelium", "URL",
Lyudec99f8b72016-10-18 14:12:09 -04001512 &error);
1513 if (!chamelium->url) {
1514 igt_warn("Couldn't read chamelium URL from config file: %s\n",
1515 error->message);
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001516 return false;
Lyudec99f8b72016-10-18 14:12:09 -04001517 }
1518
Paul Kocialkowskiebd6eb62017-07-04 16:33:19 +03001519 return chamelium_read_port_mappings(chamelium, drm_fd);
Lyudec99f8b72016-10-18 14:12:09 -04001520}
1521
1522/**
1523 * chamelium_reset:
1524 * @chamelium: The Chamelium instance to use
1525 *
1526 * Resets the chamelium's IO board. As well, this also has the effect of
1527 * causing all of the chamelium ports to get set to unplugged
1528 */
1529void chamelium_reset(struct chamelium *chamelium)
1530{
1531 igt_debug("Resetting the chamelium\n");
1532 xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Reset", "()"));
1533}
1534
1535static void chamelium_exit_handler(int sig)
1536{
1537 igt_debug("Deinitializing Chamelium\n");
1538
1539 if (cleanup_instance)
1540 chamelium_deinit(cleanup_instance);
1541}
1542
1543/**
1544 * chamelium_init:
1545 * @chamelium: The Chamelium instance to use
1546 * @drm_fd: a display initialized with #igt_display_init
1547 *
1548 * Sets up a connection with a chamelium, using the URL specified in the
1549 * Chamelium configuration. This must be called first before trying to use the
1550 * chamelium.
1551 *
1552 * If we fail to establish a connection with the chamelium, fail to find a
1553 * configured connector, etc. we fail the current test.
1554 *
1555 * Returns: A newly initialized chamelium struct, or NULL on error
1556 */
1557struct chamelium *chamelium_init(int drm_fd)
1558{
1559 struct chamelium *chamelium = malloc(sizeof(struct chamelium));
1560
1561 if (!chamelium)
1562 return NULL;
1563
1564 /* A chamelium instance was set up previously, so clean it up before
1565 * starting a new one
1566 */
1567 if (cleanup_instance)
1568 chamelium_deinit(cleanup_instance);
1569
1570 memset(chamelium, 0, sizeof(*chamelium));
1571 chamelium->drm_fd = drm_fd;
1572
1573 /* Setup the libxmlrpc context */
1574 xmlrpc_env_init(&chamelium->env);
1575 xmlrpc_client_setup_global_const(&chamelium->env);
1576 xmlrpc_client_create(&chamelium->env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE,
1577 PACKAGE_VERSION, NULL, 0, &chamelium->client);
1578 if (chamelium->env.fault_occurred) {
1579 igt_debug("Failed to init xmlrpc: %s\n",
1580 chamelium->env.fault_string);
1581 goto error;
1582 }
1583
1584 if (!chamelium_read_config(chamelium, drm_fd))
1585 goto error;
1586
Lyudec99f8b72016-10-18 14:12:09 -04001587 cleanup_instance = chamelium;
1588 igt_install_exit_handler(chamelium_exit_handler);
1589
1590 return chamelium;
1591
1592error:
1593 xmlrpc_env_clean(&chamelium->env);
1594 free(chamelium);
1595
1596 return NULL;
1597}
1598
1599/**
1600 * chamelium_deinit:
1601 * @chamelium: The Chamelium instance to use
1602 *
1603 * Frees the resources used by a connection to the chamelium that was set up
1604 * with #chamelium_init. As well, this function restores the state of the
1605 * chamelium like it was before calling #chamelium_init. This function is also
1606 * called as an exit handler, so users only need to call manually if they don't
1607 * want the chamelium interfering with other tests in the same file.
1608 */
1609void chamelium_deinit(struct chamelium *chamelium)
1610{
1611 int i;
1612 struct chamelium_edid *pos, *tmp;
1613
1614 /* We want to make sure we leave all of the ports plugged in, since
1615 * testing setups requiring multiple monitors are probably using the
1616 * chamelium to provide said monitors
1617 */
1618 chamelium_reset(chamelium);
1619 for (i = 0; i < chamelium->port_count; i++)
1620 chamelium_plug(chamelium, &chamelium->ports[i]);
1621
1622 /* Destroy any EDIDs we created to make sure we don't leak them */
1623 igt_list_for_each_safe(pos, tmp, &chamelium->edids->link, link) {
1624 chamelium_destroy_edid(chamelium, pos->id);
1625 free(pos);
1626 }
1627
1628 xmlrpc_client_destroy(chamelium->client);
1629 xmlrpc_env_clean(&chamelium->env);
1630
1631 for (i = 0; i < chamelium->port_count; i++)
1632 free(chamelium->ports[i].name);
1633
1634 free(chamelium->ports);
1635 free(chamelium);
1636}
1637
1638igt_constructor {
1639 /* Frame dumps can be large, so we need to be able to handle very large
1640 * responses
1641 *
Paul Kocialkowskie246ff02017-07-20 18:13:37 +03001642 * Limit here is 15MB
Lyudec99f8b72016-10-18 14:12:09 -04001643 */
Paul Kocialkowskie246ff02017-07-20 18:13:37 +03001644 xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 15728640);
Lyudec99f8b72016-10-18 14:12:09 -04001645}