Well I'm definitely in agreement with the idea of using config files for this. Would be a lot more reliable then these tricks. Will respin with this added On Mon, 2016-11-14 at 08:05 +0100, Daniel Vetter wrote: > On Mon, Nov 07, 2016 at 07:05:16PM -0500, Lyude wrote: > > > > For the purpose of testing things such as hotplugging and bad > > monitors, > > the ChromeOS team ended up designing a neat little device known as > > the > > Chamelium. More information on this can be found here: > > > > https://www.chromium.org/chromium-os/testing/chamelium > > > > This adds support for a couple of things to intel-gpu-tools: > > - igt library functions for connecting to udev and monitoring it > > for > > hotplug events, loosely based off of the unfinished hotplugging > > implementation in testdisplay > > - Library functions for controlling the chamelium in tests using > > xmlrpc. A couple of RPC calls were ommitted here, mainly because > > they > > didn't seem very useful for our needs or because they're just > > plain > > broken > > - A set of basic tests using the chamelium. > > > > Because there's no surefire way that I know of where we can map > > which > > chamelium port belongs to which port on the system being tested (we > > could just use hotplugging, but then we'd be relying on something > > that > > might be broken on the machine and potentially give false positives > > for > > certain tests), most of the chamelium tests will figure out whether > > or > > not a connection happened by counting the number of connectors > > matching > > the status we're looking for before hotplugging with the chamelium, > > vs. > > after hotplugging it. > > > > Tests which require that we know which port belongs to a certain > > port > > (such as ones where we actually perform a modeset) will unplug all > > of > > the chamelium ports, plug the desired port, then use the first DRM > > connector with the desired connector type that's marked as > > connected. In > > order to ensure we don't end up using the wrong connector, these > > tests > > will skip if they find any connectors with the desired type marked > > as > > connected before performing the hotplug on the chamelium. > > > > Running these tests requires (of course) a working Chamelium, along > > with > > the RPC URL for the chamelium being specified in the environment > > variable CHAMELIUM_HOST. If no URL is specified, the tests will > > just > > skip on their own. As well, tests for connectors which are not > > actually > > present on the system or the chamelium will skip on their own as > > well. > > > > Signed-off-by: Lyude <lyude@xxxxxxxxxx> > > --- > > configure.ac | 13 + > > lib/Makefile.am | 10 +- > > lib/igt.h | 1 + > > lib/igt_chamelium.c | 628 > > +++++++++++++++++++++++++++++++++++++++++++++++++ > > lib/igt_chamelium.h | 77 ++++++ > > Since you typed these nice gtkdocs, please also add it to the .xml in > docs/ and make sure it looks all good (./autogen.sh --enable-gtk- > docs). > > Wrt the api itself I think all we need is agreement from Tomeu that > this > is the right thing for his chamelium use-cases, too. And Tomeu has > commit > rights, so can push this stuff for you. > -Daniel > > > > > > lib/igt_kms.c | 107 +++++++++ > > lib/igt_kms.h | 13 +- > > scripts/run-tests.sh | 4 +- > > tests/Makefile.am | 5 +- > > tests/Makefile.sources | 1 + > > tests/chamelium.c | 549 > > ++++++++++++++++++++++++++++++++++++++++++ > > 11 files changed, 1403 insertions(+), 5 deletions(-) > > create mode 100644 lib/igt_chamelium.c > > create mode 100644 lib/igt_chamelium.h > > create mode 100644 tests/chamelium.c > > > > diff --git a/configure.ac b/configure.ac > > index 735cfd5..88113b2 100644 > > --- a/configure.ac > > +++ b/configure.ac > > @@ -259,6 +259,18 @@ if test "x$with_libunwind" = xyes; then > > AC_MSG_ERROR([libunwind not found. Use > > --without-libunwind to disable libunwind support.])) > > fi > > > > +# enable support for using the chamelium > > +AC_ARG_ENABLE(chamelium, > > + AS_HELP_STRING([--without-chamelium], > > + [Build tests without chamelium > > support]), > > + [], [with_chamelium=yes]) > > + > > +AM_CONDITIONAL(HAVE_CHAMELIUM, [test "x$with_chamelium" = xyes]) > > +if test "x$with_chamelium" = xyes; then > > + AC_DEFINE(HAVE_CHAMELIUM, 1, [chamelium suport]) > > + PKG_CHECK_MODULES(XMLRPC, xmlrpc_client) > > +fi > > + > > # enable debug symbols > > AC_ARG_ENABLE(debug, > > AS_HELP_STRING([--disable-debug], > > @@ -356,6 +368,7 @@ echo " Assembler : > > ${enable_assembler}" > > echo " Debugger : ${enable_debugger}" > > echo " Overlay : X: ${enable_overlay_xlib}, Xv: > > ${enable_overlay_xvlib}" > > echo " x86-specific tools : ${build_x86}" > > +echo " Chamelium support : ${with_chamelium}" > > echo "" > > echo " • API-Documentation : ${enable_gtk_doc}" > > echo " • Fail on warnings : ${enable_werror}" > > diff --git a/lib/Makefile.am b/lib/Makefile.am > > index 4c0893d..aeac43a 100644 > > --- a/lib/Makefile.am > > +++ b/lib/Makefile.am > > @@ -22,8 +22,14 @@ if !HAVE_LIBDRM_INTEL > > stubs/drm/intel_bufmgr.h > > endif > > > > +if HAVE_CHAMELIUM > > + libintel_tools_la_SOURCES += \ > > + igt_chamelium.c \ > > + igt_chamelium.h > > +endif > > + > > AM_CPPFLAGS = -I$(top_srcdir) > > -AM_CFLAGS = $(CWARNFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) > > $(LIBUNWIND_CFLAGS) $(DEBUG_CFLAGS) \ > > +AM_CFLAGS = $(CWARNFLAGS) $(DRM_CFLAGS) $(PCIACCESS_CFLAGS) > > $(LIBUNWIND_CFLAGS) $(DEBUG_CFLAGS) $(XMLRPC_CFLAGS) $(UDEV_CFLAGS) > > \ > > -DIGT_SRCDIR=\""$(abs_top_srcdir)/tests"\" \ > > -DIGT_DATADIR=\""$(pkgdatadir)"\" \ > > -DIGT_LOG_DOMAIN=\""$(subst _,-,$*)"\" \ > > @@ -38,5 +44,7 @@ libintel_tools_la_LIBADD = \ > > $(LIBUDEV_LIBS) \ > > $(LIBUNWIND_LIBS) \ > > $(TIMER_LIBS) \ > > + $(XMLRPC_LIBS) \ > > + $(UDEV_LIBS) \ > > -lm > > > > diff --git a/lib/igt.h b/lib/igt.h > > index d751f24..0ea03e4 100644 > > --- a/lib/igt.h > > +++ b/lib/igt.h > > @@ -30,6 +30,7 @@ > > #include "igt_aux.h" > > #include "igt_core.h" > > #include "igt_core.h" > > +#include "igt_chamelium.h" > > #include "igt_debugfs.h" > > #include "igt_draw.h" > > #include "igt_fb.h" > > diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c > > new file mode 100644 > > index 0000000..a281ef6 > > --- /dev/null > > +++ b/lib/igt_chamelium.c > > @@ -0,0 +1,628 @@ > > +/* > > + * Copyright © 2016 Red Hat Inc. > > + * > > + * Permission is hereby granted, free of charge, to any person > > obtaining a > > + * copy of this software and associated documentation files (the > > "Software"), > > + * to deal in the Software without restriction, including without > > limitation > > + * the rights to use, copy, modify, merge, publish, distribute, > > sublicense, > > + * and/or sell copies of the Software, and to permit persons to > > whom the > > + * Software is furnished to do so, subject to the following > > conditions: > > + * > > + * The above copyright notice and this permission notice > > (including the next > > + * paragraph) shall be included in all copies or substantial > > portions of the > > + * Software. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > > EXPRESS OR > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > > MERCHANTABILITY, > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > > EVENT SHALL > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > > DAMAGES OR OTHER > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > > ARISING > > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > > OTHER DEALINGS > > + * IN THE SOFTWARE. > > + * > > + * Authors: > > + * Lyude Paul <lyude@xxxxxxxxxx> > > + */ > > + > > +#include "config.h" > > + > > +#include <string.h> > > +#include <errno.h> > > +#include <xmlrpc-c/base.h> > > +#include <xmlrpc-c/client.h> > > + > > +#include "igt.h" > > + > > +#define check_rpc() \ > > + igt_assert_f(!env.fault_occurred, "Chamelium RPC call > > failed: %s\n", \ > > + env.fault_string); > > + > > +/** > > + * chamelium_ports: > > + * > > + * Contains information on all of the ports that are physically > > connected from > > + * the chamelium to the system. This information is initialized > > when > > + * #chamelium_init is called. > > + */ > > +struct chamelium_port *chamelium_ports; > > + > > +/** > > + * chamelium_port_count: > > + * > > + * How many ports are physically connected from the chamelium to > > the system. > > + */ > > +int chamelium_port_count; > > + > > +static const char *chamelium_url; > > +static xmlrpc_env env; > > + > > +struct chamelium_edid { > > + int id; > > + struct igt_list link; > > +}; > > +struct chamelium_edid *allocated_edids; > > + > > +/** > > + * chamelium_plug: > > + * @id: The ID of the port on the chamelium to plug in > > + * > > + * Simulate a display connector being plugged into the system > > using the > > + * chamelium. > > + */ > > +void chamelium_plug(int id) > > +{ > > + xmlrpc_value *res; > > + > > + igt_debug("Plugging port %d\n", id); > > + res = xmlrpc_client_call(&env, chamelium_url, "Plug", > > "(i)", id); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_unplug: > > + * @id: The ID of the port on the chamelium to unplug > > + * > > + * Simulate a display connector being unplugged from the system > > using the > > + * chamelium. > > + */ > > +void chamelium_unplug(int id) > > +{ > > + xmlrpc_value *res; > > + > > + igt_debug("Unplugging port %d\n", id); > > + res = xmlrpc_client_call(&env, chamelium_url, "Unplug", > > "(i)", id); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_is_plugged: > > + * @id: The ID of the port on the chamelium to check the status of > > + * > > + * Check whether or not the given port has been plugged into the > > system using > > + * #chamelium_plug. > > + * > > + * Returns: True if the connector is set to plugged in, false > > otherwise. > > + */ > > +bool chamelium_is_plugged(int id) > > +{ > > + xmlrpc_value *res; > > + xmlrpc_bool is_plugged; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, "IsPlugged", > > "(i)", id); > > + check_rpc(); > > + > > + xmlrpc_read_bool(&env, res, &is_plugged); > > + xmlrpc_DECREF(res); > > + > > + return is_plugged; > > +} > > + > > +/** > > + * chamelium_port_wait_video_input_stable: > > + * @id: The ID of the port on the chamelium to check the status of > > + * @timeout_secs: How long to wait for a video signal to appear > > before timing > > + * out > > + * > > + * Waits for a video signal to appear on the given port. This is > > useful for > > + * checking whether or not we've setup a monitor correctly. > > + * > > + * Returns: True if a video signal was detected, false if we timed > > out > > + */ > > +bool chamelium_port_wait_video_input_stable(int id, int > > timeout_secs) > > +{ > > + xmlrpc_value *res; > > + xmlrpc_bool is_on; > > + > > + igt_debug("Waiting for video input to stabalize on port > > %d\n", id); > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "WaitVideoInputStable", > > + "(ii)", id, timeout_secs); > > + check_rpc(); > > + > > + xmlrpc_read_bool(&env, res, &is_on); > > + xmlrpc_DECREF(res); > > + > > + return is_on; > > +} > > + > > +/** > > + * chamelium_fire_hpd_pulses: > > + * @id: The ID of the port to fire hotplug pulses on > > + * @width_msec: How long each pulse should last > > + * @count: The number of pulses to send > > + * > > + * A convienence function for sending multiple hotplug pulses to > > the system. > > + * The pulses start at low (e.g. connector is disconnected), and > > then alternate > > + * from high (e.g. connector is plugged in) to low. This is the > > equivalent of > > + * repeatedly calling #chamelium_plug and #chamelium_unplug, > > waiting > > + * @width_msec between each call. > > + * > > + * If @count is even, the last pulse sent will be high, and if > > it's odd then it > > + * will be low. Resetting the HPD line back to it's previous > > state, if desired, > > + * is the responsibility of the caller. > > + */ > > +void chamelium_fire_hpd_pulses(int port, int width_msec, int > > count) > > +{ > > + xmlrpc_value *pulse_widths = xmlrpc_array_new(&env), > > + *width = xmlrpc_int_new(&env, width_msec), > > *res; > > + int i; > > + > > + igt_debug("Firing %d HPD pulses with width of %d msec on > > port %d\n", > > + count, width_msec, port); > > + > > + for (i = 0; i < count; i++) > > + xmlrpc_array_append_item(&env, pulse_widths, > > width); > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "FireMixedHpdPulses", > > + "(iA)", port, pulse_widths); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > + xmlrpc_DECREF(width); > > + xmlrpc_DECREF(pulse_widths); > > +} > > + > > +/** > > + * chamelium_fire_mixed_hpd_pulses: > > + * @id: The ID of the port to fire hotplug pulses on > > + * @...: The length of each pulse in milliseconds, terminated with > > a %0 > > + * > > + * Does the same thing as #chamelium_fire_hpd_pulses, but allows > > the caller to > > + * specify the length of each individual pulse. > > + */ > > +void chamelium_fire_mixed_hpd_pulses(int id, ...) > > +{ > > + va_list args; > > + xmlrpc_value *pulse_widths = xmlrpc_array_new(&env), > > *width, *res; > > + int arg; > > + > > + igt_debug("Firing mixed HPD pulses on port %d\n", id); > > + > > + va_start(args, id); > > + for (arg = va_arg(args, int); arg; arg = va_arg(args, > > int)) { > > + width = xmlrpc_int_new(&env, arg); > > + xmlrpc_array_append_item(&env, pulse_widths, > > width); > > + xmlrpc_DECREF(width); > > + } > > + va_end(args); > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "FireMixedHpdPulses", > > + "(iA)", id, pulse_widths); > > + check_rpc(); > > + xmlrpc_DECREF(res); > > + > > + xmlrpc_DECREF(pulse_widths); > > +} > > + > > +static void async_rpc_handler(const char *server_url, const char > > *method_name, > > + xmlrpc_value *param_array, void > > *user_data, > > + xmlrpc_env *fault, xmlrpc_value > > *result) > > +{ > > + /* We don't care about the responses */ > > +} > > + > > +/** > > + * chamelium_async_hpd_pulse_start: > > + * @id: The ID of the port to fire a hotplug pulse on > > + * @high: Whether to fire a high pulse (e.g. simulate a connect), > > or a low > > + * pulse (e.g. simulate a disconnect) > > + * @delay_secs: How long to wait before sending the HPD pulse. > > + * > > + * Instructs the chamelium to send an hpd pulse after @delay_secs > > seconds have > > + * passed, without waiting for the chamelium to finish. This is > > useful for > > + * testing things such as hpd after a suspend/resume cycle, since > > we can't tell > > + * the chamelium to send a hotplug at the same time that our > > system is > > + * suspended. > > + * > > + * It is required that the user eventually call > > + * #chamelium_async_hpd_pulse_finish, to clean up the leftover > > XML-RPC > > + * responses from the chamelium. > > + */ > > +void chamelium_async_hpd_pulse_start(int id, bool high, int > > delay_secs) > > +{ > > + xmlrpc_value *pulse_widths = xmlrpc_array_new(&env), > > *width; > > + > > + /* TODO: Actually implement something in the chameleon > > server to allow > > + * for delayed actions such as hotplugs. This would work a > > bit better > > + * and allow us to test suspend/resume on ports without > > hpd like VGA > > + */ > > + > > + igt_debug("Sending HPD pulse (%s) on port %d with %d > > second delay\n", > > + high ? "high->low" : "low->high", id, > > delay_secs); > > + > > + /* If we're starting at high, make the first pulse width 0 > > so we keep > > + * the port connected */ > > + if (high) { > > + width = xmlrpc_int_new(&env, 0); > > + xmlrpc_array_append_item(&env, pulse_widths, > > width); > > + xmlrpc_DECREF(width); > > + } > > + > > + width = xmlrpc_int_new(&env, delay_secs * 1000); > > + xmlrpc_array_append_item(&env, pulse_widths, width); > > + xmlrpc_DECREF(width); > > + > > + xmlrpc_client_call_asynch(chamelium_url, > > "FireMixedHpdPulses", > > + async_rpc_handler, NULL, "(iA)", > > + id, pulse_widths); > > + xmlrpc_DECREF(pulse_widths); > > +} > > + > > +/** > > + * chamelium_async_hpd_pulse_finish: > > + * > > + * Waits for any asynchronous RPC started by > > #chamelium_async_hpd_pulse_start > > + * to complete, and then cleans up any leftover responses from the > > chamelium. > > + * If all of the RPC calls have already completed, this function > > returns > > + * immediately. > > + */ > > +void chamelium_async_hpd_pulse_finish(void) > > +{ > > + xmlrpc_client_event_loop_finish_asynch(); > > +} > > + > > +/** > > + * chamelium_new_edid: > > + * @edid: The edid blob to upload to the chamelium > > + * > > + * Uploads and registers a new EDID with the chamelium. The EDID > > will be > > + * destroyed automatically when #chamelium_deinit is called. > > + * > > + * Returns: The ID of the EDID uploaded to the chamelium. > > + */ > > +int chamelium_new_edid(const unsigned char *edid) > > +{ > > + xmlrpc_value *res; > > + struct chamelium_edid *allocated_edid; > > + int edid_id; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "CreateEdid", > > + "(6)", edid, EDID_LENGTH); > > + check_rpc(); > > + > > + xmlrpc_read_int(&env, res, &edid_id); > > + xmlrpc_DECREF(res); > > + > > + allocated_edid = malloc(sizeof(struct chamelium_edid)); > > + igt_assert(allocated_edid); > > + > > + allocated_edid->id = edid_id; > > + if (allocated_edids) { > > + igt_list_insert(&allocated_edids->link, > > &allocated_edid->link); > > + } else { > > + igt_list_init(&allocated_edid->link); > > + allocated_edids = allocated_edid; > > + } > > + > > + return edid_id; > > +} > > + > > +static void chamelium_destroy_edid(int edid_id) > > +{ > > + xmlrpc_value *res; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "DestroyEdid", > > + "(i)", edid_id); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_port_set_edid: > > + * @id: The ID of the port to set the EDID on > > + * @edid_id: The ID of an EDID on the chamelium created with > > + * #chamelium_new_edid, or 0 to disable the EDID on the port > > + * > > + * Sets a port on the chamelium to use the specified EDID. This > > does not fire a > > + * hotplug pulse on it's own, and merely changes what EDID the > > chamelium port > > + * will report to us the next time we probe it. Users will need to > > reprobe the > > + * connectors themselves if they want to see the EDID reported by > > the port > > + * change. > > + */ > > +void chamelium_port_set_edid(int id, int edid_id) > > +{ > > + xmlrpc_value *res; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, "ApplyEdid", > > + "(ii)", id, edid_id); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_port_set_ddc_state: > > + * @id: The ID of the port whose DDC bus we want to modify > > + * @enabled: Whether or not to enable the DDC bus > > + * > > + * This disables the DDC bus (e.g. the i2c line on the connector > > that gives us > > + * an EDID) of the specified port on the chamelium. This is useful > > for testing > > + * behavior on legacy connectors such as VGA, where the presence > > of a DDC bus > > + * is not always guaranteed. > > + */ > > +void chamelium_port_set_ddc_state(int port, bool enabled) > > +{ > > + xmlrpc_value *res; > > + > > + igt_debug("%sabling DDC bus on port %d\n", > > + enabled ? "En" : "Dis", port); > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "SetDdcState", > > + "(ib)", port, enabled); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_port_get_ddc_state: > > + * @id: The ID of the port whose DDC bus we want to check the > > status of > > + * > > + * Check whether or not the DDC bus on the specified chamelium > > port is enabled > > + * or not. > > + * > > + * Returns: True if the DDC bus is enabled, false otherwise. > > + */ > > +bool chamelium_port_get_ddc_state(int id) > > +{ > > + xmlrpc_value *res; > > + xmlrpc_bool enabled; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "IsDdcEnabled", > > + "(i)", id); > > + check_rpc(); > > + > > + xmlrpc_read_bool(&env, res, &enabled); > > + > > + xmlrpc_DECREF(res); > > + return enabled; > > +} > > + > > +/** > > + * chamelium_port_get_resolution: > > + * @id: The ID of the port whose display resolution we want to > > check > > + * @x: Where to store the horizontal resolution of the port > > + * @y: Where to store the verical resolution of the port > > + * > > + * Check the current reported display resolution of the specified > > port on the > > + * chamelium. This information is provided by the chamelium > > itself, not DRM. > > + * Useful for verifying that we really are scanning out at the > > resolution we > > + * think we are. > > + */ > > +void chamelium_port_get_resolution(int id, int *x, int *y) > > +{ > > + xmlrpc_value *res, *res_x, *res_y; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "DetectResolution", > > + "(i)", id); > > + check_rpc(); > > + > > + xmlrpc_array_read_item(&env, res, 0, &res_x); > > + xmlrpc_array_read_item(&env, res, 1, &res_y); > > + xmlrpc_read_int(&env, res_x, x); > > + xmlrpc_read_int(&env, res_y, y); > > + > > + xmlrpc_DECREF(res_x); > > + xmlrpc_DECREF(res_y); > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_get_crc_for_area: > > + * @id: The ID of the port from which we want to retrieve the CRC > > + * @x: The X coordinate on the emulated display to start > > calculating the CRC > > + * from > > + * @y: The Y coordinate on the emulated display to start > > calculating the CRC > > + * from > > + * @w: The width of the area to fetch the CRC from > > + * @h: The height of the area to fetch the CRC from > > + * > > + * Reads back the pixel CRC for an area on the specified chamelium > > port. This > > + * is the same as using the CRC readback from a GPU, the main > > difference being > > + * the data is provided by the chamelium and also allows us to > > specify a region > > + * of the screen to use as opposed to the entire thing. > > + * > > + * Returns: The CRC read back from the chamelium > > + */ > > +unsigned int chamelium_get_crc_for_area(int id, int x, int y, int > > w, int h) > > +{ > > + xmlrpc_value *res; > > + unsigned int crc; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "ComputePixelChecksum", > > + "(iiiii)", id, x, y, w, h); > > + check_rpc(); > > + > > + xmlrpc_read_int(&env, res, (int*)(&crc)); > > + > > + xmlrpc_DECREF(res); > > + return crc; > > +} > > + > > +static unsigned int chamelium_get_port_type(int port) > > +{ > > + xmlrpc_value *res; > > + const char *port_type_str; > > + unsigned int port_type; > > + > > + res = xmlrpc_client_call(&env, chamelium_url, > > "GetConnectorType", > > + "(i)", port); > > + check_rpc(); > > + > > + xmlrpc_read_string(&env, res, &port_type_str); > > + igt_debug("Port %d is of type '%s'\n", port, > > port_type_str); > > + > > + if (strcmp(port_type_str, "DP") == 0) > > + port_type = DRM_MODE_CONNECTOR_DisplayPort; > > + else if (strcmp(port_type_str, "HDMI") == 0) > > + port_type = DRM_MODE_CONNECTOR_HDMIA; > > + else if (strcmp(port_type_str, "VGA") == 0) > > + port_type = DRM_MODE_CONNECTOR_VGA; > > + else > > + port_type = DRM_MODE_CONNECTOR_Unknown; > > + > > + free((void*)port_type_str); > > + xmlrpc_DECREF(res); > > + > > + return port_type; > > +} > > + > > +static void chamelium_probe_ports(void) > > +{ > > + xmlrpc_value *res, *port_val; > > + struct chamelium_port *port; > > + unsigned int port_type; > > + int id, i, len; > > + > > + /* Figure out what ports are connected, along with their > > types */ > > + res = xmlrpc_client_call(&env, chamelium_url, > > "ProbeInputs", "()"); > > + check_rpc(); > > + > > + len = xmlrpc_array_size(&env, res); > > + chamelium_ports = calloc(sizeof(struct chamelium_port), > > len); > > + > > + igt_assert(chamelium_ports); > > + > > + for (i = 0; i < len; i++) { > > + xmlrpc_array_read_item(&env, res, i, &port_val); > > + xmlrpc_read_int(&env, port_val, &id); > > + xmlrpc_DECREF(port_val); > > + > > + port_type = chamelium_get_port_type(id); > > + if (port_type == DRM_MODE_CONNECTOR_Unknown) > > + continue; > > + > > + port = &chamelium_ports[chamelium_port_count]; > > + port->id = id; > > + port->type = port_type; > > + port->original_plugged = chamelium_is_plugged(id); > > + chamelium_port_count++; > > + } > > + > > + chamelium_ports = realloc(chamelium_ports, > > + sizeof(struct chamelium_port) * > > + chamelium_port_count); > > + igt_assert(chamelium_ports); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +/** > > + * chamelium_reset: > > + * > > + * Resets the chamelium's IO board. As well, this also has the > > effect of > > + * causing all of the chamelium ports to get set to unplugged > > + */ > > +void chamelium_reset(void) > > +{ > > + xmlrpc_value *res; > > + > > + igt_debug("Resetting the chamelium\n"); > > + > > + res = xmlrpc_client_call(&env, chamelium_url, "Reset", > > "()"); > > + check_rpc(); > > + > > + xmlrpc_DECREF(res); > > +} > > + > > +static void chamelium_exit_handler(int sig) > > +{ > > + chamelium_deinit(); > > +} > > + > > +/** > > + * chamelium_init: > > + * > > + * Sets up a connection with a chamelium, using the url provided > > in the > > + * CHAMELIUM_HOST enviornment variable. This must be called first > > before trying > > + * to use the chamelium. When the connection is no longer needed, > > the user > > + * should call #chamelium_deinit to free the resources used by the > > connection. > > + * > > + * If we fail to establish a connection with the chamelium, we > > fail the current > > + * test. > > + */ > > +void chamelium_init(void) > > +{ > > + chamelium_url = getenv("CHAMELIUM_HOST"); > > + igt_assert(chamelium_url != NULL); > > + > > + xmlrpc_env_init(&env); > > + > > + xmlrpc_client_init2(&env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE, > > + PACKAGE_VERSION, NULL, 0); > > + igt_fail_on_f(env.fault_occurred, > > + "Failed to init xmlrpc: %s\n", > > + env.fault_string); > > + > > + chamelium_probe_ports(); > > + chamelium_reset(); > > + > > + igt_install_exit_handler(chamelium_exit_handler); > > +} > > + > > +/** > > + * chamelium_deinit: > > + * > > + * Frees the resources used by a connection to the chamelium that > > was set up > > + * with #chamelium_init. As well, this function restores the state > > of the > > + * chamelium like it was before calling #chamelium_init. This > > function is also > > + * called as an exit handler, so users only need to call manually > > if they don't > > + * want the chamelium interfering with other tests in the same > > file. > > + */ > > +void chamelium_deinit(void) > > +{ > > + int i; > > + struct chamelium_edid *pos, *tmp; > > + > > + if (!chamelium_url) > > + return; > > + > > + /* Restore the original state of all of the chamelium > > ports */ > > + igt_debug("Restoring original state of chamelium\n"); > > + chamelium_reset(); > > + for (i = 0; i < chamelium_port_count; i++) { > > + if (chamelium_ports[i].original_plugged) > > + chamelium_plug(chamelium_ports[i].id); > > + } > > + > > + /* Destroy any EDIDs we created to make sure we don't leak > > them */ > > + igt_list_for_each_safe(pos, tmp, &allocated_edids->link, > > link) { > > + chamelium_destroy_edid(pos->id); > > + free(pos); > > + } > > + > > + xmlrpc_client_cleanup(); > > + xmlrpc_env_clean(&env); > > + > > + free(chamelium_ports); > > + allocated_edids = NULL; > > + chamelium_url = NULL; > > + chamelium_ports = NULL; > > + chamelium_port_count = 0; > > +} > > + > > diff --git a/lib/igt_chamelium.h b/lib/igt_chamelium.h > > new file mode 100644 > > index 0000000..900615c > > --- /dev/null > > +++ b/lib/igt_chamelium.h > > @@ -0,0 +1,77 @@ > > +/* > > + * Copyright © 2016 Red Hat Inc. > > + * > > + * Permission is hereby granted, free of charge, to any person > > obtaining a > > + * copy of this software and associated documentation files (the > > "Software"), > > + * to deal in the Software without restriction, including without > > limitation > > + * the rights to use, copy, modify, merge, publish, distribute, > > sublicense, > > + * and/or sell copies of the Software, and to permit persons to > > whom the > > + * Software is furnished to do so, subject to the following > > conditions: > > + * > > + * The above copyright notice and this permission notice > > (including the next > > + * paragraph) shall be included in all copies or substantial > > portions of the > > + * Software. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > > EXPRESS OR > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > > MERCHANTABILITY, > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > > EVENT SHALL > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > > DAMAGES OR OTHER > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > > ARISING > > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > > OTHER DEALINGS > > + * IN THE SOFTWARE. > > + * > > + * Authors: Lyude Paul <lyude@xxxxxxxxxx> > > + */ > > + > > +#ifndef IGT_CHAMELIUM_H > > +#define IGT_CHAMELIUM_H > > + > > +#include "config.h" > > +#include "igt.h" > > +#include <stdbool.h> > > + > > +/** > > + * chamelium_port: > > + * @type: The DRM connector type of the chamelium port > > + * @id: The ID of the chamelium port > > + */ > > +struct chamelium_port { > > + unsigned int type; > > + int id; > > + > > + /* For restoring the original port state after finishing > > tests */ > > + bool original_plugged; > > +}; > > + > > +extern int chamelium_port_count; > > +extern struct chamelium_port *chamelium_ports; > > + > > +/** > > + * igt_require_chamelium: > > + * > > + * Checks whether or not the environment variable CHAMELIUM_HOST > > is non-null, > > + * otherwise skips the current test. > > + */ > > +#define igt_require_chamelium() \ > > + igt_require(getenv("CHAMELIUM_HOST") != NULL); > > + > > +void chamelium_init(void); > > +void chamelium_deinit(void); > > +void chamelium_reset(void); > > + > > +void chamelium_plug(int id); > > +void chamelium_unplug(int id); > > +bool chamelium_is_plugged(int id); > > +bool chamelium_port_wait_video_input_stable(int id, int > > timeout_secs); > > +void chamelium_fire_mixed_hpd_pulses(int id, ...); > > +void chamelium_fire_hpd_pulses(int id, int width, int count); > > +void chamelium_async_hpd_pulse_start(int id, bool high, int > > delay_secs); > > +void chamelium_async_hpd_pulse_finish(void); > > +int chamelium_new_edid(const unsigned char *edid); > > +void chamelium_port_set_edid(int id, int edid_id); > > +bool chamelium_port_get_ddc_state(int id); > > +void chamelium_port_set_ddc_state(int id, bool enabled); > > +void chamelium_port_get_resolution(int id, int *x, int *y); > > +unsigned int chamelium_get_crc_for_area(int id, int x, int y, int > > w, int h); > > + > > +#endif /* IGT_CHAMELIUM_H */ > > diff --git a/lib/igt_kms.c b/lib/igt_kms.c > > index 989704e..7768d7b 100644 > > --- a/lib/igt_kms.c > > +++ b/lib/igt_kms.c > > @@ -40,6 +40,10 @@ > > #endif > > #include <errno.h> > > #include <time.h> > > +#ifdef HAVE_CHAMELIUM > > +#include <libudev.h> > > +#include <poll.h> > > +#endif > > > > #include <i915_drm.h> > > > > @@ -2760,6 +2764,109 @@ void igt_reset_connectors(void) > > "detect"); > > } > > > > +#ifdef HAVE_CHAMELIUM > > +static struct udev_monitor *hotplug_mon; > > + > > +/** > > + * igt_watch_hotplug: > > + * > > + * Begin monitoring udev for hotplug events. > > + */ > > +void igt_watch_hotplug(void) > > +{ > > + struct udev *udev; > > + int ret, flags, fd; > > + > > + if (hotplug_mon) > > + igt_cleanup_hotplug(); > > + > > + udev = udev_new(); > > + igt_assert(udev != NULL); > > + > > + hotplug_mon = udev_monitor_new_from_netlink(udev, "udev"); > > + igt_assert(hotplug_mon != NULL); > > + > > + ret = > > udev_monitor_filter_add_match_subsystem_devtype(hotplug_mon, > > + "drm > > ", > > + "drm > > _minor"); > > + igt_assert_eq(ret, 0); > > + ret = udev_monitor_filter_update(hotplug_mon); > > + igt_assert_eq(ret, 0); > > + ret = udev_monitor_enable_receiving(hotplug_mon); > > + igt_assert_eq(ret, 0); > > + > > + /* Set the fd for udev as non blocking */ > > + fd = udev_monitor_get_fd(hotplug_mon); > > + flags = fcntl(fd, F_GETFL, 0); > > + igt_assert(flags); > > + > > + flags |= O_NONBLOCK; > > + igt_assert_neq(fcntl(fd, F_SETFL, flags), -1); > > +} > > + > > +/** > > + * igt_hotplug_detected: > > + * @timeout_secs: How long to wait for a hotplug event to occur. > > + * > > + * Assert that a hotplug event was received since we last checked > > the monitor. > > + */ > > +bool igt_hotplug_detected(int timeout_secs) > > +{ > > + struct udev_device *dev; > > + const char *hotplug_val; > > + struct pollfd fd = { > > + .fd = udev_monitor_get_fd(hotplug_mon), > > + .events = POLLIN > > + }; > > + bool hotplug_received = false; > > + > > + /* Go through all of the events pending on the udev > > monitor. Once we > > + * receive a hotplug, we continue going through the rest > > of the events > > + * so that redundant hotplug events don't change the > > results of future > > + * checks > > + */ > > + while (!hotplug_received && poll(&fd, 1, timeout_secs * > > 1000)) { > > + dev = udev_monitor_receive_device(hotplug_mon); > > + > > + hotplug_val = udev_device_get_property_value(dev, > > "HOTPLUG"); > > + if (hotplug_val && atoi(hotplug_val) == 1) > > + hotplug_received = true; > > + > > + udev_device_unref(dev); > > + } > > + > > + return hotplug_received; > > +} > > + > > +/** > > + * igt_flush_hotplugs: > > + * @mon: A udev monitor created by #igt_watch_hotplug > > + * > > + * Get rid of any pending hotplug events waiting on the udev > > monitor > > + */ > > +void igt_flush_hotplugs(void) > > +{ > > + struct udev_device *dev; > > + > > + while ((dev = udev_monitor_receive_device(hotplug_mon))) > > + udev_device_unref(dev); > > +} > > + > > +/** > > + * igt_cleanup_hotplug: > > + * > > + * Cleanup the resources allocated by #igt_watch_hotplug > > + */ > > +void igt_cleanup_hotplug(void) > > +{ > > + struct udev *udev = udev_monitor_get_udev(hotplug_mon); > > + > > + udev_monitor_unref(hotplug_mon); > > + hotplug_mon = NULL; > > + udev_unref(udev); > > +} > > +#endif > > + > > /** > > * kmstest_get_vbl_flag: > > * @pipe_id: Pipe to convert to flag representation. > > diff --git a/lib/igt_kms.h b/lib/igt_kms.h > > index 6422adc..d0b67e0 100644 > > --- a/lib/igt_kms.h > > +++ b/lib/igt_kms.h > > @@ -31,6 +31,9 @@ > > #include <stdbool.h> > > #include <stdint.h> > > #include <stddef.h> > > +#ifdef HAVE_CHAMELIUM > > +#include <libudev.h> > > +#endif > > > > #include <xf86drmMode.h> > > > > @@ -333,6 +336,7 @@ igt_plane_t *igt_output_get_plane(igt_output_t > > *output, enum igt_plane plane); > > bool igt_pipe_get_property(igt_pipe_t *pipe, const char *name, > > uint32_t *prop_id, uint64_t *value, > > drmModePropertyPtr *prop); > > +void igt_output_get_edid(igt_output_t *output, unsigned char > > *edid_out); > > > > static inline bool igt_plane_supports_rotation(igt_plane_t *plane) > > { > > @@ -478,6 +482,13 @@ uint32_t kmstest_get_vbl_flag(uint32_t > > pipe_id); > > #define EDID_LENGTH 128 > > const unsigned char* igt_kms_get_base_edid(void); > > const unsigned char* igt_kms_get_alt_edid(void); > > - > > +bool igt_compare_output_edid(igt_output_t *output, const unsigned > > char *edid); > > + > > +#ifdef HAVE_CHAMELIUM > > +void igt_watch_hotplug(void); > > +bool igt_hotplug_detected(int timeout_secs); > > +void igt_flush_hotplugs(void); > > +void igt_cleanup_hotplug(void); > > +#endif > > > > #endif /* __IGT_KMS_H__ */ > > diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh > > index 97ba9e5..6539bf9 100755 > > --- a/scripts/run-tests.sh > > +++ b/scripts/run-tests.sh > > @@ -122,10 +122,10 @@ if [ ! -x "$PIGLIT" ]; then > > fi > > > > if [ "x$RESUME" != "x" ]; then > > - sudo IGT_TEST_ROOT="$IGT_TEST_ROOT" "$PIGLIT" resume > > "$RESULTS" $NORETRY > > + sudo IGT_TEST_ROOT="$IGT_TEST_ROOT" > > CHAMELIUM_HOST="$CHAMELIUM_HOST" "$PIGLIT" resume "$RESULTS" > > $NORETRY > > else > > mkdir -p "$RESULTS" > > - sudo IGT_TEST_ROOT="$IGT_TEST_ROOT" "$PIGLIT" run igt -o > > "$RESULTS" -s $VERBOSE $EXCLUDE $FILTER > > + sudo IGT_TEST_ROOT="$IGT_TEST_ROOT" > > CHAMELIUM_HOST="$CHAMELIUM_HOST" "$PIGLIT" run igt -o "$RESULTS" -s > > $VERBOSE $EXCLUDE $FILTER > > fi > > > > if [ "$SUMMARY" == "html" ]; then > > diff --git a/tests/Makefile.am b/tests/Makefile.am > > index a408126..06a8e6b 100644 > > --- a/tests/Makefile.am > > +++ b/tests/Makefile.am > > @@ -63,7 +63,7 @@ AM_CFLAGS = $(DRM_CFLAGS) $(CWARNFLAGS) -Wno- > > unused-result $(DEBUG_CFLAGS)\ > > $(LIBUNWIND_CFLAGS) $(WERROR_CFLAGS) \ > > $(NULL) > > > > -LDADD = ../lib/libintel_tools.la $(GLIB_LIBS) > > +LDADD = ../lib/libintel_tools.la $(GLIB_LIBS) $(XMLRPC_LIBS) > > > > AM_CFLAGS += $(CAIRO_CFLAGS) $(LIBUDEV_CFLAGS) $(GLIB_CFLAGS) > > AM_LDFLAGS = -Wl,--as-needed > > @@ -119,5 +119,8 @@ vc4_wait_bo_CFLAGS = $(AM_CFLAGS) > > $(DRM_VC4_CFLAGS) > > vc4_wait_bo_LDADD = $(LDADD) $(DRM_VC4_LIBS) > > vc4_wait_seqno_CFLAGS = $(AM_CFLAGS) $(DRM_VC4_CFLAGS) > > vc4_wait_seqno_LDADD = $(LDADD) $(DRM_VC4_LIBS) > > + > > +chamelium_CFLAGS = $(AM_CFLAGS) $(XMLRPC_CFLAGS) $(UDEV_CFLAGS) > > +chamelium_LDADD = $(LDADD) $(XMLRPC_LIBS) $(UDEV_LIBS) > > endif > > > > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > > index 6d081c3..3e01852 100644 > > --- a/tests/Makefile.sources > > +++ b/tests/Makefile.sources > > @@ -131,6 +131,7 @@ TESTS_progs_M = \ > > template \ > > vgem_basic \ > > vgem_slow \ > > + chamelium \ > > $(NULL) > > > > TESTS_progs_XM = \ > > diff --git a/tests/chamelium.c b/tests/chamelium.c > > new file mode 100644 > > index 0000000..769cfdc > > --- /dev/null > > +++ b/tests/chamelium.c > > @@ -0,0 +1,549 @@ > > +/* > > + * Copyright © 2016 Red Hat Inc. > > + * > > + * Permission is hereby granted, free of charge, to any person > > obtaining a > > + * copy of this software and associated documentation files (the > > "Software"), > > + * to deal in the Software without restriction, including without > > limitation > > + * the rights to use, copy, modify, merge, publish, distribute, > > sublicense, > > + * and/or sell copies of the Software, and to permit persons to > > whom the > > + * Software is furnished to do so, subject to the following > > conditions: > > + * > > + * The above copyright notice and this permission notice > > (including the next > > + * paragraph) shall be included in all copies or substantial > > portions of the > > + * Software. > > + * > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, > > EXPRESS OR > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF > > MERCHANTABILITY, > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO > > EVENT SHALL > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, > > DAMAGES OR OTHER > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, > > ARISING > > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR > > OTHER DEALINGS > > + * IN THE SOFTWARE. > > + * > > + * Authors: > > + * Lyude Paul <lyude@xxxxxxxxxx> > > + */ > > + > > +#include "config.h" > > +#include "igt.h" > > + > > +#include <fcntl.h> > > +#include <string.h> > > + > > +struct connector_info { > > + int id; > > + unsigned int type; > > +}; > > + > > +typedef struct { > > + int drm_fd; > > + struct connector_info *connectors; > > + int connector_count; > > +} data_t; > > + > > +#define HOTPLUG_TIMEOUT 30 /* seconds */ > > + > > +/* > > + * Since we can't get an exact mapping of which chamelium ports > > are connected > > + * to each of the DUT's ports, we have to figure out whether or > > not the status > > + * of a port on the chamelium has changed by counting the number > > of connectors > > + * with the connector type and status we want, and then comparing > > the values > > + * from before hotplugging and after > > + */ > > +static void > > +reprobe_connectors(data_t *data, unsigned int type) > > +{ > > + drmModeConnector *connector; > > + int i; > > + > > + igt_debug("Reprobing %s connectors...\n", > > + kmstest_connector_type_str(type)); > > + > > + for (i = 0; i < data->connector_count; i++) { > > + if (data->connectors[i].type != type) > > + continue; > > + > > + connector = drmModeGetConnector(data->drm_fd, > > + data- > > >connectors[i].id); > > + igt_assert(connector); > > + > > + drmModeFreeConnector(connector); > > + } > > +} > > + > > +static void > > +reset_chamelium_state(data_t *data) > > +{ > > + chamelium_reset(); > > + reprobe_connectors(data, DRM_MODE_CONNECTOR_DisplayPort); > > + reprobe_connectors(data, DRM_MODE_CONNECTOR_HDMIA); > > + reprobe_connectors(data, DRM_MODE_CONNECTOR_VGA); > > +} > > + > > +static int > > +connector_status_count(data_t *data, unsigned int type, unsigned > > int status) > > +{ > > + struct connector_info *info; > > + drmModeConnector *connector; > > + int count = 0; > > + > > + for (int i = 0; i < data->connector_count; i++) { > > + info = &data->connectors[i]; > > + if (info->type != type) > > + continue; > > + > > + connector = drmModeGetConnectorCurrent(data- > > >drm_fd, info->id); > > + igt_assert(connector); > > + > > + if (connector->connection == status) > > + count++; > > + > > + drmModeFreeConnector(connector); > > + } > > + > > + return count; > > +} > > + > > +static void > > +require_connector_present(data_t *data, unsigned int type) > > +{ > > + int i; > > + bool found = false; > > + > > + for (i = 0; i < data->connector_count && !found; i++) { > > + if (data->connectors[i].type == type) > > + found = true; > > + } > > + > > + igt_require_f(found, "No port of type %s was found on the > > system\n", > > + kmstest_connector_type_str(type)); > > + > > + for (i = 0, found = false; i < chamelium_port_count && > > !found; i++) { > > + if (chamelium_ports[i].type == type) > > + found = true; > > + } > > + > > + igt_require_f(found, "No connected port of type %s was > > found on the chamelium\n", > > + kmstest_connector_type_str(type)); > > +} > > + > > +static drmModeConnector * > > +find_connected(data_t *data, unsigned int type) > > +{ > > + drmModeConnector *connector; > > + int i; > > + > > + for (i = 0; i < data->connector_count; i++) { > > + if (data->connectors[i].type != type) > > + continue; > > + > > + connector = drmModeGetConnector(data->drm_fd, > > + data- > > >connectors[i].id); > > + igt_assert(connector); > > + > > + if (connector->connection == DRM_MODE_CONNECTED) > > + return connector; > > + > > + drmModeFreeConnector(connector); > > + } > > + > > + return NULL; > > +} > > + > > +/* > > + * Skips the test if we find any connectors with a matching type > > connected. > > + * This is necessary when we need to identify which port on the > > machine is > > + * connected to which port on the chamelium, since any other ports > > that are > > + * connected to other displays could cause us to choose the wrong > > port. > > + * > > + * This also has the effect of reprobing all of the connected > > ports. > > + */ > > +static void > > +skip_on_any_connected(data_t *data, unsigned int type) > > +{ > > + drmModeConnector *connector; > > + > > + connector = find_connected(data, type); > > + if (connector) > > + drmModeFreeConnector(connector); > > + > > + igt_skip_on(connector); > > +} > > + > > +static void > > +test_basic_hotplug(data_t *data, struct chamelium_port *port) > > +{ > > + int before, after; > > + int i; > > + > > + reset_chamelium_state(data); > > + igt_watch_hotplug(); > > + > > + for (i = 0; i < 15; i++) { > > + igt_flush_hotplugs(); > > + > > + /* Check if we get a sysfs hotplug event */ > > + before = connector_status_count(data, port->type, > > + DRM_MODE_CONNECTED > > ); > > + chamelium_plug(port->id); > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > + > > + /* Now we should have one additional port > > connected */ > > + reprobe_connectors(data, port->type); > > + after = connector_status_count(data, port->type, > > + DRM_MODE_CONNECTED) > > ; > > + igt_assert_lt(before, after); > > + > > + igt_flush_hotplugs(); > > + > > + /* Now check if we get a hotplug from > > disconnection */ > > + before = connector_status_count(data, port->type, > > + DRM_MODE_DISCONNEC > > TED); > > + chamelium_unplug(port->id); > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > + > > + /* And make sure we now have one more disconnected > > port */ > > + reprobe_connectors(data, port->type); > > + after = connector_status_count(data, port->type, > > + DRM_MODE_DISCONNECT > > ED); > > + igt_assert_lt(before, after); > > + > > + /* Sleep so we don't accidentally cause an hpd > > storm */ > > + sleep(1); > > + } > > +} > > + > > +static void > > +test_edid_read(data_t *data, struct chamelium_port *port, > > + int edid_id, const unsigned char *edid) > > +{ > > + drmModeConnector *connector; > > + drmModeObjectProperties *props; > > + drmModePropertyBlobPtr edid_blob = NULL; > > + bool edid_found = false; > > + int i; > > + > > + reset_chamelium_state(data); > > + skip_on_any_connected(data, port->type); > > + > > + chamelium_port_set_edid(port->id, edid_id); > > + chamelium_plug(port->id); > > + sleep(1); > > + igt_assert(connector = find_connected(data, port->type)); > > + > > + props = drmModeObjectGetProperties(data->drm_fd, > > + connector- > > >connector_id, > > + DRM_MODE_OBJECT_CONNECT > > OR); > > + igt_assert(props); > > + > > + /* Get the edid */ > > + for (i = 0; i < props->count_props && !edid_blob; i++) { > > + drmModePropertyPtr prop = > > + drmModeGetProperty(data->drm_fd, > > + props->props[i]); > > + > > + igt_assert(prop); > > + > > + if (strcmp(prop->name, "EDID") == 0) { > > + edid_blob = drmModeGetPropertyBlob( > > + data->drm_fd, props->prop_values[i]); > > + } > > + > > + drmModeFreeProperty(prop); > > + } > > + > > + /* And make sure it matches to what we expected */ > > + edid_found = memcmp(edid, edid_blob->data, EDID_LENGTH) == > > 0; > > + > > + drmModeFreePropertyBlob(edid_blob); > > + drmModeFreeObjectProperties(props); > > + drmModeFreeConnector(connector); > > + > > + igt_assert(edid_found); > > +} > > + > > +static void > > +test_suspend_resume_hpd(data_t *data, struct chamelium_port *port, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test) > > +{ > > + int before, after; > > + int delay = 7; > > + > > + igt_skip_without_suspend_support(state, test); > > + reset_chamelium_state(data); > > + igt_watch_hotplug(); > > + > > + igt_set_autoresume_delay(15); > > + > > + /* Make sure we notice new connectors after resuming */ > > + before = connector_status_count(data, port->type, > > DRM_MODE_CONNECTED); > > + sleep(1); > > + igt_flush_hotplugs(); > > + > > + chamelium_async_hpd_pulse_start(port->id, false, delay); > > + igt_system_suspend_autoresume(state, test); > > + chamelium_async_hpd_pulse_finish(); > > + > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > + > > + reprobe_connectors(data, port->type); > > + after = connector_status_count(data, port->type, > > DRM_MODE_CONNECTED); > > + igt_assert_lt(before, after); > > + > > + igt_flush_hotplugs(); > > + > > + /* Now make sure we notice disconnected connectors after > > resuming */ > > + before = connector_status_count(data, port->type, > > DRM_MODE_DISCONNECTED); > > + > > + chamelium_async_hpd_pulse_start(port->id, true, delay); > > + igt_system_suspend_autoresume(state, test); > > + chamelium_async_hpd_pulse_finish(); > > + > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > + > > + reprobe_connectors(data, port->type); > > + after = connector_status_count(data, port->type, > > DRM_MODE_DISCONNECTED); > > + igt_assert_lt(before, after); > > +} > > + > > +static void > > +test_suspend_resume_edid_change(data_t *data, struct > > chamelium_port *port, > > + enum igt_suspend_state state, > > + enum igt_suspend_test test, > > + int edid_id, > > + int alt_edid_id) > > +{ > > + igt_skip_without_suspend_support(state, test); > > + reset_chamelium_state(data); > > + igt_watch_hotplug(); > > + > > + /* First plug in the port */ > > + chamelium_port_set_edid(port->id, edid_id); > > + chamelium_plug(port->id); > > + > > + reprobe_connectors(data, port->type); > > + sleep(1); > > + igt_flush_hotplugs(); > > + > > + /* > > + * Change the edid before we suspend. On resume, the > > machine should > > + * notice the EDID change and fire a hotplug event. > > + */ > > + chamelium_port_set_edid(port->id, alt_edid_id); > > + > > + igt_system_suspend_autoresume(state, test); > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > +} > > + > > +static void > > +test_display(data_t *data, struct chamelium_port *port) > > +{ > > + igt_display_t display; > > + igt_output_t *output; > > + igt_plane_t *primary; > > + struct igt_fb fb; > > + drmModeRes *res; > > + drmModeModeInfo *mode; > > + int connector_found = false, fb_id; > > + > > + chamelium_plug(port->id); > > + igt_assert(res = drmModeGetResources(data->drm_fd)); > > + kmstest_unset_all_crtcs(data->drm_fd, res); > > + > > + igt_display_init(&display, data->drm_fd); > > + > > + /* Find the active connector */ > > + for_each_connected_output(&display, output) { > > + drmModeConnector *connector = output- > > >config.connector; > > + > > + if (connector && connector->connector_type == > > port->type && > > + connector->connection == DRM_MODE_CONNECTED) { > > + connector_found = true; > > + break; > > + } > > + } > > + igt_assert(connector_found); > > + > > + /* Setup the display */ > > + igt_output_set_pipe(output, PIPE_A); > > + mode = igt_output_get_mode(output); > > + primary = igt_output_get_plane(output, IGT_PLANE_PRIMARY); > > + igt_assert(primary); > > + > > + fb_id = igt_create_pattern_fb(data->drm_fd, > > + mode->hdisplay, > > + mode->vdisplay, > > + DRM_FORMAT_XRGB8888, > > + LOCAL_DRM_FORMAT_MOD_NONE, > > + &fb); > > + igt_assert(fb_id > 0); > > + igt_plane_set_fb(primary, &fb); > > + > > + igt_display_commit(&display); > > + > > + igt_assert(chamelium_port_wait_video_input_stable(port- > > >id, > > + HOTPLUG_ > > TIMEOUT)); > > + > > + drmModeFreeResources(res); > > + igt_display_fini(&display); > > +} > > + > > +static void > > +test_hpd_without_ddc(data_t *data, struct chamelium_port *port) > > +{ > > + reset_chamelium_state(data); > > + igt_watch_hotplug(); > > + > > + /* Disable the DDC on the connector and make sure we still > > get a > > + * hotplug > > + */ > > + chamelium_port_set_ddc_state(port->id, false); > > + chamelium_plug(port->id); > > + > > + igt_assert(igt_hotplug_detected(HOTPLUG_TIMEOUT)); > > +} > > + > > +static void > > +cache_connector_info(data_t *data) > > +{ > > + drmModeRes *res = drmModeGetResources(data->drm_fd); > > + drmModeConnector *connector; > > + int i; > > + > > + igt_assert(res); > > + > > + data->connector_count = res->count_connectors; > > + data->connectors = calloc(sizeof(struct connector_info), > > + res->count_connectors); > > + igt_assert(data->connectors); > > + > > + for (i = 0; i < res->count_connectors; i++) { > > + connector = drmModeGetConnectorCurrent(data- > > >drm_fd, > > + res- > > >connectors[i]); > > + igt_assert(connector); > > + > > + data->connectors[i].id = connector->connector_id; > > + data->connectors[i].type = connector- > > >connector_type; > > + > > + drmModeFreeConnector(connector); > > + } > > + > > + drmModeFreeResources(res); > > +} > > + > > +#define for_each_port(p, port) \ > > + for (p = 0, port = &chamelium_ports[p]; \ > > + p < chamelium_port_count; \ > > + p++, port = &chamelium_ports[p]) \ > > + > > +#define connector_subtest(name__, type__) \ > > + igt_subtest(name__) \ > > + for_each_port(p, port) \ > > + if (port->type == DRM_MODE_CONNECTOR_ ## > > type__) > > + > > +#define define_common_connector_tests(type_str__, > > type__) \ > > + connector_subtest(type_str__ "-hpd", > > type__) \ > > + test_basic_hotplug(&data, > > port); \ > > + > > \ > > + connector_subtest(type_str__ "-edid-read", type__) > > { \ > > + test_edid_read(&data, port, > > edid_id, \ > > + igt_kms_get_base_edid()); > > \ > > + test_edid_read(&data, port, > > alt_edid_id, \ > > + igt_kms_get_alt_edid()); > > \ > > + } > > \ > > + > > \ > > + connector_subtest(type_str__ "-hpd-after-suspend", > > type__) \ > > + test_suspend_resume_hpd(&data, > > port, \ > > + SUSPEND_STATE_MEM, > > \ > > + SUSPEND_TEST_NONE); > > \ > > + > > \ > > + connector_subtest(type_str__ "-hpd-after-hibernate", > > type__) \ > > + test_suspend_resume_hpd(&data, > > port, \ > > + SUSPEND_STATE_DISK, > > \ > > + SUSPEND_TEST_DEVICES); > > \ > > + > > \ > > + connector_subtest(type_str__ "-edid-change-during- > > suspend", type__) \ > > + test_suspend_resume_edid_change(&data, > > port, \ > > + SUSPEND_STATE_MEM, > > \ > > + SUSPEND_TEST_NONE, > > \ > > + edid_id, > > alt_edid_id); \ > > + > > \ > > + connector_subtest(type_str__ "-edid-change-during- > > hibernate", type__) \ > > + test_suspend_resume_edid_change(&data, > > port, \ > > + SUSPEND_STATE_DISK > > , \ > > + SUSPEND_TEST_DEVIC > > ES, \ > > + edid_id, > > alt_edid_id); \ > > + > > \ > > + connector_subtest(type_str__ "-display", > > type__) \ > > + test_display(&data, port); > > + > > +static data_t data; > > + > > +igt_main > > +{ > > + struct chamelium_port *port; > > + int edid_id, alt_edid_id, p; > > + > > + igt_fixture { > > + igt_require_chamelium(); > > + igt_skip_on_simulation(); > > + > > + chamelium_init(); > > + > > + edid_id = > > chamelium_new_edid(igt_kms_get_base_edid()); > > + alt_edid_id = > > chamelium_new_edid(igt_kms_get_alt_edid()); > > + > > + data.drm_fd = > > drm_open_driver_master(DRIVER_INTEL); > > + cache_connector_info(&data); > > + > > + /* So fbcon doesn't try to reprobe things itself > > */ > > + kmstest_set_vt_graphics_mode(); > > + } > > + > > + igt_subtest_group { > > + igt_fixture { > > + require_connector_present( > > + &data, > > DRM_MODE_CONNECTOR_DisplayPort); > > + } > > + > > + define_common_connector_tests("dp", DisplayPort); > > + } > > + > > + igt_subtest_group { > > + igt_fixture { > > + require_connector_present( > > + &data, DRM_MODE_CONNECTOR_HDMIA); > > + } > > + > > + define_common_connector_tests("hdmi", HDMIA); > > + } > > + > > + igt_subtest_group { > > + igt_fixture { > > + require_connector_present( > > + &data, DRM_MODE_CONNECTOR_VGA); > > + } > > + > > + connector_subtest("vga-hpd", VGA) > > + test_basic_hotplug(&data, port); > > + > > + connector_subtest("vga-edid-read", VGA) { > > + test_edid_read(&data, port, edid_id, > > + igt_kms_get_base_edid()); > > + test_edid_read(&data, port, alt_edid_id, > > + igt_kms_get_alt_edid()); > > + } > > + > > + /* FIXME: Right now there isn't a way to do any > > sort of delayed > > + * psuedo-hotplug with VGA, so testing detection > > after a > > + * suspend/resume cycle isn't possible yet > > + */ > > + > > + connector_subtest("vga-hpd-without-ddc", VGA) > > + test_hpd_without_ddc(&data, port); > > + > > + connector_subtest("vga-display", VGA) > > + test_display(&data, port); > > + } > > +} > > -- > > 2.7.4 > > > > _______________________________________________ > > Intel-gfx mailing list > > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > > https://lists.freedesktop.org/mailman/listinfo/intel-gfx > -- Cheers, Lyude _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx