Message ID | 1428681259-21013-2-git-send-email-tprevite@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 10 April 2015 at 16:54, Todd Previte <tprevite@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@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@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@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@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
On 4/17/2015 6:44 AM, Thomas Wood wrote: > On 10 April 2015 at 16:54, Todd Previte <tprevite@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@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. Do you have a standard template for the i-g-t makefile or is it just a matter of cloning an existing one? It should probably be an optional component of the test suite. Displayport compliance testing requires dedicated HW and SW that isn't generally available to individual developers (mostly because it's rather expensive to obtain). So having it as part of a default installation is probably not worth doing. > >> + >> +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. They always sneak in there. I'll scrub it again for whitespace errors. > >> + * Performs Displayport compliance testing for the Intel i915 driver >> + * >> + * Copyright ? 2014-2015 Intel Corporation > I guess '?' should be '©'. Must have happened when the text encoding changed. I think it's UTF-8 now. Do you have a preference for text file formats within i-g-t? >> + * >> + * 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@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. Oh that might have been the auto-align "feature" in my editor. I'll go fix these. >> + >> +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. Ahhh I knew i-g-t must have something in there to handle this. I'll have a look at it and see what it'll take to use that instead. > >> + 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. Works for me. I'll put this on the list. >> + >> + 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. Ok that seems like a better solution. With the early revisions of this app, I recall some issues in trying to open DRM devices. So changing this out for known-good code from i-g-t is the way to go. > >> + >> + 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@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@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. I recently updated this on my Github repo version. It uses code from testdisplay.c and some that I wrote, which seems to work just fine. Ctrl-C and a sig handler would be fine too, but I wanted the option of accepting input should that become necessary later down the road for additional compliance testing. >> + >> + 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). I will definitely look into this. Thank you for the suggestion. > >> + 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. This was the main motivation for removing the original input handler code. So instead of trying to figure out if there's a problem, I removed it and changed over to the code from testdisplay and my stuff that I mentioned further up. > >> + >> +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 Thank you for the review, Thomas! I will post the updated version to the list but if you can always find the latest version on my Github repo here: https://github.com/tprevite/intel-gpu-tools/tree/dp_compliance Oddly, that link should have been in the cover letter for this patch series but isn't there. I'll fix that for the next major revision.
On 10/04/15 16:54, Todd Previte 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. > > Signed-off-by: Todd Previte <tprevite@gmail.com> > --- > dp_compliance/README | 102 ++++++ Just one small but important typo I noticed in this file ... > 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” s/test/text/ .Dave.
On 4/17/2015 11:42 AM, Dave Gordon wrote: > On 10/04/15 16:54, Todd Previte 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. >> >> Signed-off-by: Todd Previte <tprevite@gmail.com> >> --- >> dp_compliance/README | 102 ++++++ > Just one small but important typo I noticed in this file ... > >> 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” > s/test/text/ > > .Dave. Good catch, Dave, thank you! I'll get that fixed immediately. -T
On 17 April 2015 at 18:52, Todd Previte <tprevite@gmail.com> wrote: > > > On 4/17/2015 6:44 AM, Thomas Wood wrote: >> >> On 10 April 2015 at 16:54, Todd Previte <tprevite@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@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. > > Do you have a standard template for the i-g-t makefile or is it just a > matter of cloning an existing one? Just a standard automake Makefile.am. This is what I used: bin_PROGRAMS=dp_compliance dp_compliance_SOURCES=dp_compliance.c input.c AM_CFLAGS=$(DRM_CFLAGS) $(DEBUG_CFLAGS) $(CWARNFLAGS) LDADD=$(DRM_LIBS) Then added to the file to AC_CONFIG_FILES in configure.ac and the directory to SUBDIRS in the toplevel Makefile.am. > > It should probably be an optional component of the test suite. Displayport > compliance testing requires dedicated HW and SW that isn't generally > available to individual developers (mostly because it's rather expensive to > obtain). So having it as part of a default installation is probably not > worth doing. Ensuring it is not installed is just a matter of adding the noinst_ prefix to bin_PROGRAMS. Since there are no software dependencies, building it by default makes sense to ensure it is kept up-to-date with any i-g-t library changes. >> >> >>> + >>> +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. > > They always sneak in there. I'll scrub it again for whitespace errors. >> >> >>> + * Performs Displayport compliance testing for the Intel i915 driver >>> + * >>> + * Copyright ? 2014-2015 Intel Corporation >> >> I guess '?' should be '©'. > > Must have happened when the text encoding changed. I think it's UTF-8 now. > Do you have a preference for text file formats within i-g-t? UTF-8 is preferable. > >>> + * >>> + * 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@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. > > Oh that might have been the auto-align "feature" in my editor. I'll go fix > these. > >>> + >>> +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. > > Ahhh I knew i-g-t must have something in there to handle this. I'll have a > look at it and see what it'll take to use that instead. > >> >>> + 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. > > Works for me. I'll put this on the list. > >>> + >>> + 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. > > Ok that seems like a better solution. With the early revisions of this app, > I recall some issues in trying to open DRM devices. So changing this out for > known-good code from i-g-t is the way to go. > >> >>> + >>> + 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@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@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. > > I recently updated this on my Github repo version. It uses code from > testdisplay.c and some that I wrote, which seems to work just fine. Ctrl-C > and a sig handler would be fine too, but I wanted the option of accepting > input should that become necessary later down the road for additional > compliance testing. > >>> + >>> + 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). > > I will definitely look into this. Thank you for the suggestion. > >> >>> + 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. > > This was the main motivation for removing the original input handler code. > So instead of trying to figure out if there's a problem, I removed it and > changed over to the code from testdisplay and my stuff that I mentioned > further up. > >> >>> + >>> +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 > > > Thank you for the review, Thomas! I will post the updated version to the > list but if you can always find the latest version on my Github repo here: > > https://github.com/tprevite/intel-gpu-tools/tree/dp_compliance > > > Oddly, that link should have been in the cover letter for this patch series > but isn't there. I'll fix that for the next major revision. > > > > > >
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 + +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 + * Performs Displayport compliance testing for the Intel i915 driver + * + * Copyright ? 2014-2015 Intel Corporation + * + * 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@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); + +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); + 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); + + 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; + } + } + + 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@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@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"); + + 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: + 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 +*/ + +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; +}
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. Signed-off-by: Todd Previte <tprevite@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