On 17 April 2015 at 18:52, Todd Previte <tprevite@xxxxxxxxx> wrote: > > > On 4/17/2015 6:44 AM, Thomas Wood wrote: >> >> On 10 April 2015 at 16:54, Todd Previte <tprevite@xxxxxxxxx> 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@xxxxxxxxx> >>> --- >>> 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@xxxxxxxxx> >>> + * >>> + * 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. > > > > > > _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx