diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/igt_chamelium.c | 213 |
1 files changed, 211 insertions, 2 deletions
diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c index 385a17b0..301e9d21 100644 --- a/lib/igt_chamelium.c +++ b/lib/igt_chamelium.c @@ -361,7 +361,7 @@ static xmlrpc_value *chamelium_rpc(struct chamelium *chamelium, */ void chamelium_plug(struct chamelium *chamelium, struct chamelium_port *port) { - igt_debug("Plugging %s\n", port->name); + igt_debug("Plugging %s (Chamelium port ID %d)\n", port->name, port->id); xmlrpc_DECREF(chamelium_rpc(chamelium, NULL, "Plug", "(i)", port->id)); } @@ -1960,6 +1960,212 @@ out: return ret; } +static int port_id_from_edid(int drm_fd, drmModeConnector *connector) +{ + int port_id = -1; + bool ok; + uint64_t edid_blob_id; + drmModePropertyBlobRes *edid_blob; + const struct edid *edid; + char mfg[3]; + + if (connector->connection != DRM_MODE_CONNECTED) { + igt_debug("Skipping auto-discovery for connector %s-%d: " + "connector status is not connected\n", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + return -1; + } + + ok = kmstest_get_property(drm_fd, connector->connector_id, + DRM_MODE_OBJECT_CONNECTOR, "EDID", + NULL, &edid_blob_id, NULL); + if (!ok || !edid_blob_id) { + igt_debug("Skipping auto-discovery for connector %s-%d: " + "missing the EDID property\n", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + return -1; + } + + edid_blob = drmModeGetPropertyBlob(drm_fd, edid_blob_id); + igt_assert(edid_blob); + + edid = (const struct edid *) edid_blob->data; + + edid_get_mfg(edid, mfg); + if (memcmp(mfg, "IGT", 3) != 0) { + igt_debug("Skipping connector %s-%d for auto-discovery: " + "manufacturer is %.3s, not IGT\n", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id, mfg); + goto out; + } + + if (edid->prod_code[0] != 'C' || edid->prod_code[1] != 'H') { + igt_warn("Invalid EDID for IGT connector %s-%d: " + "invalid product code\n", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + goto out; + } + + port_id = *(uint32_t *) &edid->serial; + igt_debug("Auto-discovery mapped connector %s-%d to Chamelium " + "port ID %d\n", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id, port_id); + +out: + drmModeFreePropertyBlob(edid_blob); + return port_id; +} + +/** + * chamelium_autodiscover: automagically discover the Chamelium port mapping + * + * The Chamelium API uses port IDs wheras the Device Under Test uses DRM + * connectors. We need to know which Chamelium port is plugged to a given DRM + * connector. This has typically been done via a configuration file in the + * past (see #chamelium_read_port_mappings), but this function provides an + * automatic way to do it. + * + * We will plug all Chamelium ports with a different EDID on each. Then we'll + * read the EDID on each DRM connector and infer the Chamelium port ID. + */ +static bool chamelium_autodiscover(struct chamelium *chamelium, int drm_fd) +{ + int candidate_ports[CHAMELIUM_MAX_PORTS]; + size_t candidate_ports_len; + drmModeRes *res; + drmModeConnector *connector; + struct chamelium_port *port; + size_t i, j, port_count; + int port_id; + uint32_t conn_id; + struct chamelium_edid *edid; + bool found; + uint32_t discovered_conns[CHAMELIUM_MAX_PORTS] = {0}; + char conn_name[64]; + struct timespec start; + uint64_t elapsed_ns; + + candidate_ports_len = chamelium_get_video_ports(chamelium, + candidate_ports); + + igt_debug("Starting Chamelium port auto-discovery on %zu ports\n", + candidate_ports_len); + igt_gettime(&start); + + edid = chamelium_new_edid(chamelium, igt_kms_get_base_edid()); + + /* Set EDID and plug ports we want to auto-discover */ + port_count = chamelium->port_count; + for (i = 0; i < candidate_ports_len; i++) { + port_id = candidate_ports[i]; + + /* Get or add a chamelium_port slot */ + port = NULL; + for (j = 0; j < chamelium->port_count; j++) { + if (chamelium->ports[j].id == port_id) { + port = &chamelium->ports[j]; + break; + } + } + if (!port) { + igt_assert(port_count < CHAMELIUM_MAX_PORTS); + port = &chamelium->ports[port_count]; + port_count++; + + port->id = port_id; + } + + chamelium_port_set_edid(chamelium, port, edid); + chamelium_plug(chamelium, port); + } + + /* Reprobe connectors and build the mapping */ + res = drmModeGetResources(drm_fd); + if (!res) + return false; + + for (i = 0; i < res->count_connectors; i++) { + conn_id = res->connectors[i]; + + /* Read the EDID and parse the Chamelium port ID we stored + * there. */ + connector = drmModeGetConnector(drm_fd, res->connectors[i]); + port_id = port_id_from_edid(drm_fd, connector); + drmModeFreeConnector(connector); + if (port_id < 0) + continue; + + /* If we already have a mapping from the config file, check + * that it's consistent. */ + found = false; + for (j = 0; j < chamelium->port_count; j++) { + port = &chamelium->ports[j]; + if (port->connector_id == conn_id) { + found = true; + igt_assert_f(port->id == port_id, + "Inconsistency detected in .igtrc: " + "connector %s is configured with " + "Chamelium port %d, but is " + "connected to port %d\n", + port->name, port->id, port_id); + break; + } + } + if (found) + continue; + + /* We got a new mapping */ + found = false; + for (j = 0; j < candidate_ports_len; j++) { + if (port_id == candidate_ports[j]) { + found = true; + discovered_conns[j] = conn_id; + break; + } + } + igt_assert_f(found, "Auto-discovered a port (%d) we haven't " + "setup\n", port_id); + } + + drmModeFreeResources(res); + + /* We now have a Chamelium port ID ↔ DRM connector ID mapping: + * candidate_ports contains the Chamelium port IDs and + * discovered_conns contains the DRM connector IDs. */ + for (i = 0; i < candidate_ports_len; i++) { + port_id = candidate_ports[i]; + conn_id = discovered_conns[i]; + if (!conn_id) { + continue; + } + + port = &chamelium->ports[chamelium->port_count]; + chamelium->port_count++; + + port->id = port_id; + port->type = chamelium_get_port_type(chamelium, port); + port->connector_id = conn_id; + + connector = drmModeGetConnectorCurrent(drm_fd, conn_id); + snprintf(conn_name, sizeof(conn_name), "%s-%u", + kmstest_connector_type_str(connector->connector_type), + connector->connector_type_id); + drmModeFreeConnector(connector); + port->name = strdup(conn_name); + } + + elapsed_ns = igt_nsec_elapsed(&start); + igt_debug("Auto-discovery took %fms\n", + (float) elapsed_ns / (1000 * 1000)); + + return true; +} + static bool chamelium_read_config(struct chamelium *chamelium, int drm_fd) { GError *error = NULL; @@ -1977,7 +2183,10 @@ static bool chamelium_read_config(struct chamelium *chamelium, int drm_fd) return false; } - return chamelium_read_port_mappings(chamelium, drm_fd); + if (!chamelium_read_port_mappings(chamelium, drm_fd)) { + return false; + } + return chamelium_autodiscover(chamelium, drm_fd); } /** |
