[Intel-gfx] [PATCH] igt/dp: Displayport Compliance Testing - Userspace Component
Thomas Wood
thomas.wood at intel.com
Fri Apr 17 06:44:59 PDT 2015
On 10 April 2015 at 16:54, Todd Previte <tprevite at gmail.com> wrote:
> This is the userspace component of the Displayport compliance testing
> software requried for compliance testing of the i915 driver. The README
> included in the dp_compliance/ directory contains the most up to date
> information on the use and operation of the user app.
Just a few comments below from a cursory glance through the code.
There are some places where greater use of the i-g-t library may bring
some advantages.
>
> Signed-off-by: Todd Previte <tprevite at gmail.com>
> ---
> dp_compliance/README | 102 ++++++
> dp_compliance/build.sh | 6 +
> dp_compliance/dp_compliance.c | 782 ++++++++++++++++++++++++++++++++++++++++++
> dp_compliance/input.c | 48 +++
> 4 files changed, 938 insertions(+)
> create mode 100644 dp_compliance/README
> create mode 100755 dp_compliance/build.sh
> create mode 100644 dp_compliance/dp_compliance.c
> create mode 100644 dp_compliance/input.c
>
> diff --git a/dp_compliance/README b/dp_compliance/README
> new file mode 100644
> index 0000000..1ef4913
> --- /dev/null
> +++ b/dp_compliance/README
> @@ -0,0 +1,102 @@
> +
> +Displayport Compliance Testing
> +Userspace Application
> +
> +This is the userspace portion of the Displayport Compliance testing suite for the
> +i915 driver. It must be running in order to successfully complete Displayport
> +compliance testing. This file contains the latest available information about
> +the user app and its operation.
> +
> +This app and the kernel code that accompanies it has been written to satisfy the
> +requirements of the Displayport Link CTS Core 1.2 rev1.1 specification from VESA.
> +
> +Note that this application does not support eDP compliance testing.
> +
> +Compliance testing requires several components:
> + - A kernel build that contains the patch set for DP compliance support
> + - A Displayport compliance testing appliance such as the Unigraf DPR-100,
> + DPR-120 or Quantum Data 882EDP
> + - This user application
> + - A Windows host machine to run the test software
> + - Root access on the DUT
> +
> +Test setup:
> + It is strongly recommended that the Windows host, test appliance and DUT be freshly
> + restarted before testing begins. This ensures that any previous configurations and
> + settings will not interfere with the test process. Refer to the test appliance
> + documentation for setup, software installation and operation specific to that
> + device.
> +
> + The Linux DUT must be in text (console) mode and cannot have any other display
> + manager running. The easiest way to accomplish this is to place the word “text”
> + on the command line for booting the kernel. The recommended boot parameters for
> + the kernel are as follows:
> + “test debug drm.debug=0x0e”
> + This enables debug output in the logs and will boot the system to a command prompt.
> +
> + You must be logged in as root in order to run the user app.
> +
> + Connections (required):
> + - Test appliance connected to the external Displayport connector on the DUT
> + - Test appliance connected to the Windows host (usually via USB)
> +
> + Connections (optional):
> + - SSH/telnet connection to the DUT from the Windows host or other system
> + - A display attached to the DUT for monitoring the console or running
> + the user app.
> +
> + The user app can be run directly from the console on the DUT (if a non-Displayport
> + display is attached) or from an SSH/telnet session. Generally speaking, it’s best
> + to run from an SSH session, but testing will operate identically regardless of how
> + the user app is launched.
> +
> + Once the user application is up and running, waiting for a test request, the software
> + on the Windows host can now be used to execute the compliance tests.
> +
> + TL;DR version:
> + - Install test appliance software on Windows host
> + - Adjust command line on Linux DUT as necessary to enable debug and boot
> + to the console
> + - Connect the test appliance via USB to the Windows host and a Displayport
> + cable to the DUT
> + - Reboot all systems, including the test appliance
> + - SSH into the DUT or login at the command prompt
> + - Ensure that the booted kernel contains the compliance test patch set
> + and that a version of IGT with this user app is also present
> + - Launch the user app, as root. Follow the onscreen instructions until
> + it says it’s waiting for a test to begin
> + - Execute the tests via the software on the Windows host per the
> + documentation for the test appliance
> +
> +Debugfs Files:
> +
> +The file root for all the debugfs files is:
> +
> +/sys/kernel/debug/dri/0/
> +
> +The specific files are as follows:
> +
> +i915_dp_test_active
> + A simple flag that indicates whether or not compliance testing is currently active
> + in the kernel. This flag is polled by userspace and once set, invokes the test
> + handler in the user app.
> +
> +i915_dp_test_data
> + Test data is used by the kernel to pass parameters to the user app. Currently, the
> + only parameter that is delivered is the video mode to set for the test. As more tests
> + are implemented, this value may be used for additional values or information.
> +
> +i915_dp_test_type
> + The test type variable instructs the user app as to what the requested test was
> + from the sink device. These values defined at the top of the application’s main
> + implementation file and must be kept in sync with the values defined in
> + drm_dp_helper.h.
> +
> +Test operations:
> +
> +Notes:
> + If the test_active flag is stuck, it can be cleared with this simple command:
> + echo 0 > /sys/kernel/debug/dri/0/i915_dp_test_active
> + This can happen when the test appliance is connected and a test is run, but the
> + user app isn’t running.
> +
> diff --git a/dp_compliance/build.sh b/dp_compliance/build.sh
> new file mode 100755
> index 0000000..0b317a9
> --- /dev/null
> +++ b/dp_compliance/build.sh
> @@ -0,0 +1,6 @@
> +#!/bin/sh
> +# Simple build script for the compliance app
> +# Eventually should be a makefile in IGT
Yes, this should be a Makefile!
Once common i-g-t flags are added there are a few minor compiler
warnings, mostly introduced by $(CWARNFLAGS).
You'll also need to decide if you want to install the program by
default, and if so then including a man page would be useful.
> +
> +gcc -g3 dp_compliance.c input.c -o dp_compliance_app -Wall `pkg-config --libs --cflags libdrm`
> +
> diff --git a/dp_compliance/dp_compliance.c b/dp_compliance/dp_compliance.c
> new file mode 100644
> index 0000000..7d5febb
> --- /dev/null
> +++ b/dp_compliance/dp_compliance.c
> @@ -0,0 +1,782 @@
> +/*
> + * Displayport Compliance Testing Application
A couple of complaints from git-am about trailing whitespace around
here and elsewhere.
> + * Performs Displayport compliance testing for the Intel i915 driver
> + *
> + * Copyright ? 2014-2015 Intel Corporation
I guess '?' should be '©'.
> + *
> + * 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:
> + * Todd Previte <tprevite at gmail.com>
> + *
> + * Elements of the modeset code adapted from David Herrmann's
> + * DRM modeset example
> + *
> +*/
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdbool.h>
> +
> +#include <assert.h>
> +#include <drm.h>
> +#include <xf86drm.h>
> +#include <xf86drmMode.h>
> +#include <i915_drm.h>
> +
> +#include <unistd.h>
> +#include <sys/stat.h>
> +#include <signal.h>
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <poll.h>
> +
> +#include <sys/mman.h>
> +#include <time.h>
> +
> +#define I915_DRIVER_NODE "/dev/dri/card0"
> +#define INTEL_DP_DEBUGFS_ROOT "/sys/kernel/debug/dri/0/"
> +
> +#define INTEL_DP_TEST_TYPE_FILE "i915_dp_test_type"
> +#define INTEL_DP_TEST_ACTIVE_FILE "i915_dp_test_active"
> +#define INTEL_DP_TEST_DATA_FILE "i915_dp_test_data"
> +
> +/* DRM definitions - must be kept in sync with the DRM header */
> +#define DP_TEST_LINK_TRAINING (1 << 0)
> +#define DP_TEST_LINK_VIDEO_PATTERN (1 << 1)
> +#define DP_TEST_LINK_EDID_READ (1 << 2)
> +#define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */
> +
> +#define DP_COMPLIANCE_TEST_TYPE_MASK (DP_TEST_LINK_TRAINING | \
> + DP_TEST_LINK_VIDEO_PATTERN | \
> + DP_TEST_LINK_EDID_READ | \
> + DP_TEST_LINK_PHY_TEST_PATTERN)
> +
> +/* NOTE: These must be kept in sync with the definitions in intel_dp.c */
> +#define INTEL_DP_EDID_SHIFT_MASK 0
> +#define INTEL_DP_EDID_OK (0 << INTEL_DP_EDID_SHIFT_MASK)
> +#define INTEL_DP_EDID_CORRUPT (1 << INTEL_DP_EDID_SHIFT_MASK)
> +#define INTEL_DP_RESOLUTION_SHIFT_MASK 4
> +#define INTEL_DP_RESOLUTION_PREFERRED (1 << INTEL_DP_RESOLUTION_SHIFT_MASK)
> +#define INTEL_DP_RESOLUTION_STANDARD (2 << INTEL_DP_RESOLUTION_SHIFT_MASK)
> +#define INTEL_DP_RESOLUTION_FAILSAFE (3 << INTEL_DP_RESOLUTION_SHIFT_MASK)
> +#define DP_COMPLIANCE_VIDEO_MODE_MASK (INTEL_DP_RESOLUTION_PREFERRED | \
> + INTEL_DP_RESOLUTION_STANDARD | \
> + INTEL_DP_RESOLUTION_FAILSAFE)
> +
> +enum
> +{
> + INTEL_MODE_INVALID = -1,
> + INTEL_MODE_NONE = 0,
> + INTEL_MODE_PREFERRED,
> + INTEL_MODE_STANDARD,
> + INTEL_MODE_FAILSAFE
> +} intel_display_mode;
> +
> +struct dp_connector {
> + drmModeModeInfo mode_standard, mode_preferred, mode_failsafe;
> + /* Standard and preferred frame buffer*/
> + uint32_t fb, fb_width, fb_height, fb_handle, fb_stride, fb_size;
> + uint8_t *pixmap;
> + /* Failsafe framebuffer - note this is a 16-bit buffer */
> + uint32_t failsafe_fb, failsafe_width, failsafe_height;
> + uint32_t failsafe_handle, failsafe_stride, failsafe_size;
> + uint8_t *failsafe_pixmap;
> +
> + uint32_t conn;
> + uint32_t crtc;
> +
> + drmModeCrtc *saved_crtc;
> + struct dp_connector *next;
> +};
> +
> +/* Input handling from input.c */
> +extern void reset_terminal_mode(void);
> +extern void set_conio_terminal_mode(void);
> +extern int kbhit(void);
> +extern int getch(void);
> +
> +void setup_debugfs_files(int *test_active_fd, int *test_type_fd,
> + int *test_data_fd);
> + int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector *c,
> + struct dp_connector *dp_conn);
> + int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn);
> + int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn);
> + int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c,
> + struct dp_connector *dp_conn);
> + int setup_connectors(int drv_fd, drmModeResPtr pmr);
> + int setup_drm(int *drv_fd, const char *node);
Some of the lines around here appear to be intended by one space.
> +
> +void shutdown(int drv_fd);
> +void cleanup_debugfs(void);
> +
> + int set_video_mode(int drv_fd, int mode, struct dp_connector *test_connector);
> + int check_test_active(void);
> +void clear_test_active(void);
> + int get_test_data(void);
> + int get_test_type(void);
> +
> + int process_test_request(int drv_fd, int test_type, int test_data,
> + struct dp_connector *connector);
> +
> +/* Global connector list */
> +struct dp_connector *dp_connector_list;
> +unsigned short dp_connector_count;
> +
> +/* Global file descriptors */
> +int test_active_fd, test_data_fd, test_type_fd;
> +
> +int get_test_data(void)
> +{
> + char data_in[9];
> + int bytes_read;
> + int data;
> +
> + assert(test_data_fd > 0);
> + lseek(test_data_fd, 0, SEEK_SET);
> +
> + bytes_read = read(test_data_fd, data_in, 8);
> + if (bytes_read <= 0) {
> + printf("Test type read failed - %d\r\n", bytes_read);
> + return 0;
> + }
> + data_in[8] = '\0';
> + data = strtol(data_in, NULL, 16);
> + printf("Test data = %08x\r\n", data);
Normally "\n" would suffice, but "\r\n" is needed since the terminal
is in "raw" mode. I suggest perhaps looking at
igt_debug_wait_for_keypress for a way to wait for single key entry
without putting the terminal permanently into "raw" mode.
> + return data;
> +}
> +
> +int get_test_type(void)
> +{
> + char data_in[5];
> + int bytes_read;
> + int data;
> +
> + assert(test_type_fd > 0);
> + lseek(test_type_fd, 0, SEEK_SET);
> +
> + bytes_read = read(test_type_fd, data_in, 4);
> + if (bytes_read <= 0) {
> + printf("Test type read failed - %d\r\n", bytes_read);
> + return 0;
> + }
> + data_in[4] = '\0';
> + data = strtol(data_in, NULL, 16);
> + printf("Test type = %04x\r\n", data);
> + return data;
> +}
> +
> +int process_test_request(int drv_fd, int test_type, int test_data,
> + struct dp_connector *connector)
> +{
> + int status = 0;
> + int mode;
> +
> + /* Disable the main link before starting the test
> + * Note that this should probably be signaled through a debugfs file
> + * from the kernel at the start of testing.
> + */
> + set_video_mode(drv_fd, INTEL_MODE_NONE, connector);
> +
> + switch (test_type) {
> + case DP_TEST_LINK_TRAINING:
> + /* Link training = future implementation */
> + break;
> + case DP_TEST_LINK_VIDEO_PATTERN:
> + /* Video pattern = future implementation */
> + break;
> + case DP_TEST_LINK_EDID_READ:
> + mode = (test_data & DP_COMPLIANCE_VIDEO_MODE_MASK) >>
> + INTEL_DP_RESOLUTION_SHIFT_MASK;
> + printf("EDID test received, mode %d\r\n", mode);
> + status = set_video_mode(drv_fd, mode, connector);
> + clear_test_active();
> + break;
> + case DP_TEST_LINK_PHY_TEST_PATTERN:
> + /* PHY Test Pattern = future implementation */
> + break;
> + default:
> + /* Unknown test type */
> + printf("Invalid test request. Ignored.\r\n");
> + clear_test_active();
> + break;
> + }
> +
> + return status;
> +}
> +
> +void cleanup_debugfs(void)
> +{
> + close(test_active_fd);
> + close(test_data_fd);
> + close(test_type_fd);
> +}
> +
> +int check_test_active(void)
> +{
> + char data_in[2];
> + int bytes_read;
> +
> + assert(test_active_fd > 0);
> + lseek(test_active_fd, 0, SEEK_SET);
> +
> + bytes_read = read(test_active_fd, data_in, 2);
> + if (bytes_read <= 0)
> + return 0;
> +
> + if (data_in[0] == '1')
> + return 1;
> +
> + return 0;
> +}
> +
> +void clear_test_active(void)
> +{
> + int bytes_written;
> + char data_out[2];
> +
> + assert(test_active_fd > 0);
> + lseek(test_active_fd, 0, SEEK_SET);
> +
> + data_out[0] = '0';
> + data_out[1] = '\0';
> +
> + bytes_written = write(test_active_fd, data_out, 1);
> + if (bytes_written <= 0)
> + printf("Could not clear test active flag. Write failed!\r\n");
> +}
> +
> +void setup_debugfs_files(int *test_active_fd, int *test_type_fd,
> + int *test_data_fd)
> +{
> + char debugfs_path[256];
> + char *index;
> + int offset;
> +
> + offset = strlen(INTEL_DP_DEBUGFS_ROOT);
> + index = &debugfs_path[offset];
> +
> + memset(debugfs_path, 0, 256);
> + strcpy(debugfs_path, INTEL_DP_DEBUGFS_ROOT);
igt_debugfs_open would provide some useful advantages here, such as
making sure debugfs is mounted and finding the correct minor number.
> +
> + strcpy(index, INTEL_DP_TEST_TYPE_FILE);
> + *test_type_fd = open(debugfs_path, O_RDONLY);
> + assert(*test_type_fd > 0);
> + printf("Test type path : %s\r\n", debugfs_path);
> +
> + strcpy(index, INTEL_DP_TEST_DATA_FILE);
> + *test_data_fd = open(debugfs_path, O_RDONLY);
> + assert(*test_data_fd > 0);
> + printf("Test data path : %s\r\n", debugfs_path);
> +
> + strcpy(index, INTEL_DP_TEST_ACTIVE_FILE);
> + *test_active_fd = open(debugfs_path, O_RDWR);
> + assert(*test_active_fd > 0);
> + printf("Test active path : %s\r\n", debugfs_path);
> +
> + /* Reset the active flag for safety */
> + clear_test_active();
> +}
> +
> +int setup_drm(int *drv_fd, const char *node)
> +{
> + int fd, ret;
> + uint64_t use_dumb_buffers;
> +
> + if (!drmAvailable()) {
> + printf("Error: DRM not available\r\n");
> + return -EOPNOTSUPP;
> + }
> +
> + fd = drmOpen("i915", NULL);
> +
> + if (fd < 0) {
> + fd = open(node, O_RDWR | O_CLOEXEC);
> + if (fd < 0) {
> + ret = -errno;
> + printf("Error: could not open '%s'\r\n", node);
> + return ret;
> + }
> + }
drm_open_any will search for the first i915 device, and
drm_open_any_master will also ensure it is drm master.
> +
> + drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &use_dumb_buffers);
> +
> + if (!use_dumb_buffers) {
> + printf("'%s' does not support dumb buffers\r\n", node);
> + close(fd);
> + return -EOPNOTSUPP;
> + }
> + *drv_fd = fd;
> +
> + return 0;
> +}
> +
> +int setup_connectors(int drv_fd, drmModeResPtr pmr)
> +{
> + int ret, i;
> + drmModeConnector *c;
> + struct dp_connector *dp_conn;
> +
> + pmr = drmModeGetResources(drv_fd);
> + if (!pmr) {
> + printf("ERROR: Failed to retrieve DRM resources\r\n");
> + return -1;
> + }
> +
> + for (i = 0; i < pmr->count_connectors; i++) {
> +
> + c = drmModeGetConnector(drv_fd, pmr->connectors[i]);
> + if (!c) {
> + printf("Failed to retrieve connector %u:%u\r\n",
> + i,
> + pmr->connectors[i]);
> + continue;
> + }
> +
> + if(c->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
> + continue;
> +
> + dp_conn = malloc(sizeof(*dp_conn));
> + memset(dp_conn, 0, sizeof(*dp_conn));
> + dp_conn->conn = c->connector_id;
> +
> + /* Setup the DP connector*/
> + ret = setup_dp_connector(drv_fd, pmr, c, dp_conn);
> + if (ret) {
> + if (ret != -ENOENT) {
> + errno = -ret;
> + printf("Failed to setup DP connector %u:%u\r\n",
> + i,
> + pmr->connectors[i]);
> + }
> + free(dp_conn);
> + drmModeFreeConnector(c);
> + continue;
> + }
> + else
> + dp_connector_count++;
> + /* free connector data and link device into global list */
> + drmModeFreeConnector(c);
> + dp_conn->next = dp_connector_list;
> + dp_connector_list = dp_conn;
> + }
> +
> + return 0;
> +}
> +
> +int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c,
> + struct dp_connector *dp_conn)
> +{
> + int ret, i;
> + bool found_std = false, found_fs = false;
> +
> + /* Ignore any disconnected devices */
> + if (c->connection != DRM_MODE_CONNECTED) {
> + printf("Connector %u disconnected\r\n", c->connector_id);
> + return -ENOENT;
> + }
> + printf("Connector Setup:\r\n");
> + /* Setup preferred mode - should be mode[0] in the list */
> + dp_conn->mode_preferred = c->modes[0];
> + dp_conn->fb_width = c->modes[0].hdisplay;
> + dp_conn->fb_height = c->modes[0].vdisplay;
> + printf("\tPreferred mode (mode 0) for connector %u is %ux%u\r\n",
> + c->connector_id, c->modes[0].hdisplay, c->modes[0].vdisplay);
> +
> + for (i = 1; i < c->count_modes; i++) {
> + /* Standard mode is 800x600 at 60 */
> + if (c->modes[i].hdisplay == 800 &&
> + c->modes[i].vdisplay == 600 &&
> + c->modes[i].vrefresh == 60 &&
> + found_std == false) {
> + dp_conn->mode_standard = c->modes[i];
> + printf("\tStandard mode (%d) for connector %u is %ux%u\r\n",
> + i,
> + c->connector_id,
> + c->modes[i].hdisplay,
> + c->modes[i].vdisplay);
> + found_std = true;
> + }
> + /* Failsafe mode is 640x480 at 60 */
> + if (c->modes[i].hdisplay == 640 &&
> + c->modes[i].vdisplay == 480 &&
> + c->modes[i].vrefresh == 60 &&
> + found_fs == false) {
> + dp_conn->mode_failsafe = c->modes[i];
> + dp_conn->failsafe_width = c->modes[i].hdisplay;
> + dp_conn->failsafe_height = c->modes[i].vdisplay;
> + printf("\tFailsafe mode (%d) for connector %u is %ux%u\r\n",
> + i,
> + c->connector_id,
> + c->modes[i].hdisplay,
> + c->modes[i].vdisplay);
> + }
> + }
> +
> + ret = setup_crtc_for_connector(drv_fd, mr, c, dp_conn);
> + if (ret) {
> + printf("Set CRTC for connector %u failed (%d)\r\n",
> + c->connector_id, ret);
> + return ret;
> + }
> +
> + ret = setup_framebuffers(drv_fd, dp_conn);
> + if (ret) {
> + printf("Create framebuffer for connector %u failed (%d)\r\n",
> + c->connector_id, ret);
> + return ret;
> + }
> +
> + ret = setup_failsafe_framebuffer(drv_fd, dp_conn);
> + if (ret) {
> + printf("Create failsafe framebuffer for connector %u failed (%d)\r\n",
> + c->connector_id, ret);
> + return ret;
> + }
> +
> + return ret;
> +}
> +
> +int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector *c,
> + struct dp_connector *dp_conn)
> +{
> + drmModeEncoder *drm_encoder = NULL;
> + struct dp_connector *dpc;
> + int i, j;
> + int32_t crtc;
> +
> + /* Use attached encoder if possible */
> + if (c->encoder_id)
> + drm_encoder = drmModeGetEncoder(fd, c->encoder_id);
> +
> + if (drm_encoder) {
> + if (drm_encoder->crtc_id) {
> + crtc = drm_encoder->crtc_id;
> + for (dpc = dp_connector_list; dpc; dpc = dpc->next) {
> + if (dpc->crtc == crtc) {
> + crtc = -1;
> + break;
> + }
> + }
> + if (crtc >= 0) {
> + drmModeFreeEncoder(drm_encoder);
> + dp_conn->crtc = crtc;
> + return 0;
> + }
> + }
> + drmModeFreeEncoder(drm_encoder);
> + }
> +
> + /* Check all encoder/crtc combinations */
> + for (i = 0; i < c->count_encoders; ++i) {
> + drm_encoder = drmModeGetEncoder(fd, c->encoders[i]);
> + if (!drm_encoder) {
> + continue;
> + }
> + for (j = 0; j < mr->count_crtcs; ++j) {
> + if (!(drm_encoder->possible_crtcs & (1 << j)))
> + continue;
> + crtc = mr->crtcs[j];
> + for (dpc = dp_connector_list; dpc; dpc = dpc->next) {
> + if (dpc->crtc == crtc) {
> + crtc = -1;
> + break;
> + }
> + }
> + if (crtc >= 0) {
> + drmModeFreeEncoder(drm_encoder);
> + dp_conn->crtc = crtc;
> + return 0;
> + }
> + }
> + drmModeFreeEncoder(drm_encoder);
> + }
> + printf("No CRTC available for connector %u\r\n", c->connector_id);
> + return -ENOENT;
> +}
> +
> +int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn)
> +{
> + struct drm_mode_create_dumb creq;
> + struct drm_mode_destroy_dumb dreq;
> + struct drm_mode_map_dumb mreq;
> + int ret;
> +
> + memset(&creq, 0, sizeof(creq));
> + memset(&mreq, 0, sizeof(mreq));
> +
> + creq.width = dp_conn->fb_width;
> + creq.height = dp_conn->fb_height;
> + creq.bpp = 32;
> + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
> + if (ret < 0) {
> + printf("Create dumb buffer failed\r\n");
> + return -errno;
> + }
> + dp_conn->fb_stride = creq.pitch;
> + dp_conn->fb_size = creq.size;
> + dp_conn->fb_handle = creq.handle;
> +
> + ret = drmModeAddFB(drv_fd, dp_conn->fb_width, dp_conn->fb_height,
> + 24, 32, dp_conn->fb_stride, dp_conn->fb_handle,
> + &dp_conn->fb);
> + if (ret) {
> + printf("Create framebuffer failed\r\n");
> + ret = -errno;
> + goto cleanup;
> + }
> +
> + mreq.handle = dp_conn->fb_handle;
> + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
> + if (ret) {
> + printf("Map dumb buffer failed\r\n");
> + ret = -errno;
> + goto fb_fail;
> + }
> +
> + dp_conn->pixmap = mmap(0, dp_conn->fb_size, PROT_READ | PROT_WRITE,
> + MAP_SHARED, drv_fd, mreq.offset);
> + if (dp_conn->pixmap == MAP_FAILED) {
> + printf("Mmap failed\r\n");
> + ret = -errno;
> + goto fb_fail;
> + }
> + memset(dp_conn->pixmap, 0, dp_conn->fb_size);
> + return 0;
> +
> +fb_fail:
> + drmModeRmFB(drv_fd, dp_conn->fb);
> +
> +cleanup:
> + memset(&dreq, 0, sizeof(dreq));
> + dreq.handle = dp_conn->fb_handle;
> + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
> + return ret;
> +}
> +
> +int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn)
> +{
> + struct drm_mode_create_dumb creq;
> + struct drm_mode_destroy_dumb dreq;
> + struct drm_mode_map_dumb mreq;
> + int ret;
> +
> + memset(&creq, 0, sizeof(creq));
> + creq.width = dp_conn->failsafe_width;
> + creq.height = dp_conn->failsafe_height;
> + creq.bpp = 16;
> + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
> + if (ret < 0) {
> + printf("Create dumb buffer failed\r\n");
> + return -errno;
> + }
> +
> + dp_conn->failsafe_stride = creq.pitch;
> + dp_conn->failsafe_size = creq.size;
> + dp_conn->failsafe_handle = creq.handle;
> +
> + ret = drmModeAddFB(drv_fd, dp_conn->failsafe_width,
> + dp_conn->failsafe_height, 16, 16,
> + dp_conn->failsafe_stride, dp_conn->failsafe_handle,
> + &dp_conn->failsafe_fb);
> + if (ret) {
> + printf("Create framebuffer failed\r\n");
> + ret = -errno;
> + goto cleanup;
> + }
> +
> + memset(&mreq, 0, sizeof(mreq));
> + mreq.handle = dp_conn->failsafe_handle;
> + ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
> + if (ret) {
> + printf("Map dumb buffer failed\r\n");
> + ret = -errno;
> + goto fb_fail;
> + }
> +
> + dp_conn->failsafe_pixmap = mmap(0, dp_conn->failsafe_size,
> + PROT_READ | PROT_WRITE, MAP_SHARED,
> + drv_fd, mreq.offset);
> + if (dp_conn->failsafe_pixmap == MAP_FAILED) {
> + printf("cannot mmap dumb buffer (%d): %m\r\n", errno);
> + ret = -errno;
> + goto fb_fail;
> + }
> +
> + memset(dp_conn->failsafe_pixmap, 0, dp_conn->failsafe_size);
> + return 0;
> +
> +fb_fail:
> + drmModeRmFB(drv_fd, dp_conn->failsafe_fb);
> +
> +cleanup:
> + memset(&dreq, 0, sizeof(dreq));
> + dreq.handle = dp_conn->failsafe_handle;
> + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
> + return ret;
> +}
> +
> +int set_video_mode(int drv_fd, int mode, struct dp_connector *test_connector)
> +{
> + drmModeModeInfo *requested_mode;
> + uint32_t required_fb;
> + int ret = 0;
> +
> + printf("Mode requested: ");
> + switch (mode) {
> + case INTEL_MODE_NONE:
> + printf("NONE\r\n");
> + ret = drmModeSetCrtc(drv_fd, test_connector->crtc,
> + -1, 0, 0, NULL, 0, NULL);
> + goto out;
> + break;
> + case INTEL_MODE_PREFERRED:
> + printf("PREFERRED\r\n");
> + requested_mode = &test_connector->mode_preferred;
> + required_fb = test_connector->fb;
> + break;
> + case INTEL_MODE_STANDARD:
> + printf("STANDARD\r\n");
> + requested_mode = &test_connector->mode_standard;
> + required_fb = test_connector->fb;
> + break;
> + case INTEL_MODE_FAILSAFE:
> + printf("FAILSAFE\r\n");
> + requested_mode = &test_connector->mode_failsafe;
> + required_fb = test_connector->failsafe_fb;
> + break;
> + case INTEL_MODE_INVALID:
> + default:
> + printf("INVALID! (%08x) Mode set aborted!\r\n", mode);
> + return -1;
> + break;
> + }
> + test_connector->saved_crtc = drmModeGetCrtc(drv_fd, test_connector->crtc);
> + ret = drmModeSetCrtc(drv_fd, test_connector->crtc, required_fb, 0, 0,
> + &test_connector->conn, 1, requested_mode);
> +out:
> + if (ret) {
> + printf("Failed to set CRTC for connector %u\r\n",
> + test_connector->conn);
> + }
> + return ret;
> +}
> +
> +void shutdown(int drv_fd)
> +{
> + int i;
> + struct dp_connector *index = dp_connector_list, *prev;
> + struct drm_mode_destroy_dumb dreq;
> +
> + for (i = 0; i < dp_connector_count; i++) {
> + if (index->saved_crtc) {
> + drmModeSetCrtc(drv_fd, index->saved_crtc->crtc_id,
> + index->saved_crtc->buffer_id,
> + index->saved_crtc->x,
> + index->saved_crtc->y, &index->conn, 1,
> + &index->saved_crtc->mode);
> + drmModeFreeCrtc(index->saved_crtc);
> + }
> + drmModeRmFB(drv_fd, index->fb);
> + /* Clean up dumb buffer */
> + memset(&dreq, 0, sizeof(dreq));
> + dreq.handle = index->fb_handle;
> + drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
> + prev = index;
> + index = index->next;
> + free(prev);
> + }
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int status = 0;
> + int drv_fd = 0;
> + drmModeResPtr mode_resources = NULL;
> + int done = 0;
> + char input;
> + struct dp_connector *active_connector;
> + int test_data, test_type;
> +
> + /* Setup input for keyboard handling - from input.c */
> + set_conio_terminal_mode();
> +
> + printf("ATTENTION:\r\n"
> + " Ensure that no display manager is running\r\n"
> + " App must be run as root in console/text mode\r\n"
> + " Continue? (Y/N)"
> + );
> + input = getchar();
> +
> + if (input != 'Y' && input != 'y')
> + goto exit;
> +
> + /* All-in-one setup function to get DRM off the ground */
> + setup_drm(&drv_fd, I915_DRIVER_NODE);
> +
> + /* From moderes, get the connectors, encoders, CRTCs, etc */
> + setup_connectors(drv_fd, mode_resources);
> +
> + active_connector = &dp_connector_list[0];
> +
> + setup_debugfs_files(&test_active_fd, &test_type_fd, &test_data_fd);
> +
> + printf("\r\n\r\nWaiting for test requests. 'X' or 'Q' exits\r\n");
Would the standard ctrl-c and a signal handler suffice? This would
avoid using special terminal modes. Alternatively, there is also an
example of using the GLib mainloop in testdisplay and monitoring
stdin.
> +
> + while (!done) {
> + if (kbhit()) {
> + input = getch();
> + switch (input) {
> + case 'q':
> + case 'Q':
> + case 'x':
> + case 'X':
> + done = 1;
> + break;
> + }
> + }
> + usleep(1000);
> + if (check_test_active()) {
> + test_data = get_test_data();
> + test_type = get_test_type();
> + process_test_request(drv_fd, test_type, test_data, active_connector);
> + usleep(10000);
> + }
> + }
> +
> +exit:
You might want to look at igt_install_exit_handler for this, as it
runs exit handlers from atexit as well as when a signal is received.
The terminal reset code also ought to run when a signal is received
(e.g. after ctrl-c).
> + cleanup_debugfs();
> +
> + shutdown(drv_fd);
> +
> + if (drv_fd > 0) {
> + status = drmClose(drv_fd);
> + if (status)
> + printf("Error: Failed to close i915 driver\r\n");
> + }
> +
> + printf("Compliance testing application exiting.\r\n"
> + "If testing has been performed, you may want to restart\r\n"
> + "the system to ensure it returns to normal operation.\r\n");
> +
> + return status;
> +}
> +
> diff --git a/dp_compliance/input.c b/dp_compliance/input.c
> new file mode 100644
> index 0000000..7281ce6
> --- /dev/null
> +++ b/dp_compliance/input.c
> @@ -0,0 +1,48 @@
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <sys/select.h>
> +#include <termios.h>
> +
> +/* Code sourced from stackoverflow.com -
> + http://stackoverflow.com/questions/448944/c-non-blocking-keyboard-input
> +*/
I'm not sure what the copyright status of this code is.
> +
> +struct termios orig_termios;
> +
> +void reset_terminal_mode()
> +{
> + tcsetattr(0, TCSANOW, &orig_termios);
> +}
> +
> +void set_conio_terminal_mode()
> +{
> + struct termios new_termios;
> +
> + /* take two copies - one for now, one for later */
> + tcgetattr(0, &orig_termios);
> + memcpy(&new_termios, &orig_termios, sizeof(new_termios));
> +
> + /* register cleanup handler, and set the new terminal mode */
> + atexit(reset_terminal_mode);
> + cfmakeraw(&new_termios);
> + tcsetattr(0, TCSANOW, &new_termios);
> +}
> +
> +int kbhit()
> +{
> + struct timeval tv = { 0L, 0L };
> + fd_set fds;
> + FD_ZERO(&fds);
> + FD_SET(0, &fds);
> + return select(1, &fds, NULL, NULL, &tv);
> +}
> +
> +int getch()
> +{
> + int r;
> + unsigned char c = 0;
> + if ((r = read(0, &c, sizeof(c))) < 0) {
> + return r;
> + } else return c;
> +}
> --
> 1.9.1
More information about the Intel-gfx
mailing list