[Intel-gfx] [PATCH] igt/dp: Displayport Compliance Testing - Userspace Component

Thomas Wood thomas.wood at intel.com
Mon Apr 20 07:13:48 PDT 2015


On 17 April 2015 at 18:52, Todd Previte <tprevite at gmail.com> wrote:
>
>
> On 4/17/2015 6:44 AM, Thomas Wood wrote:
>>
>> On 10 April 2015 at 16:54, Todd Previte <tprevite at gmail.com> wrote:
>>>
>>> This is the userspace component of the Displayport compliance testing
>>> software requried for compliance testing of the i915 driver. The README
>>> included in the dp_compliance/ directory contains the most up to date
>>> information on the use and operation of the user app.
>>
>> Just a few comments below from a cursory glance through the code.
>> There are some places where greater use of the i-g-t library may bring
>> some advantages.
>>
>>
>>> Signed-off-by: Todd Previte <tprevite at gmail.com>
>>> ---
>>>   dp_compliance/README          | 102 ++++++
>>>   dp_compliance/build.sh        |   6 +
>>>   dp_compliance/dp_compliance.c | 782
>>> ++++++++++++++++++++++++++++++++++++++++++
>>>   dp_compliance/input.c         |  48 +++
>>>   4 files changed, 938 insertions(+)
>>>   create mode 100644 dp_compliance/README
>>>   create mode 100755 dp_compliance/build.sh
>>>   create mode 100644 dp_compliance/dp_compliance.c
>>>   create mode 100644 dp_compliance/input.c
>>>
>>> diff --git a/dp_compliance/README b/dp_compliance/README
>>> new file mode 100644
>>> index 0000000..1ef4913
>>> --- /dev/null
>>> +++ b/dp_compliance/README
>>> @@ -0,0 +1,102 @@
>>> +
>>> +Displayport Compliance Testing
>>> +Userspace Application
>>> +
>>> +This is the userspace portion of the Displayport Compliance testing
>>> suite for the
>>> +i915 driver. It must be running in order to successfully complete
>>> Displayport
>>> +compliance testing. This file contains the latest available information
>>> about
>>> +the user app and its operation.
>>> +
>>> +This app and the kernel code that accompanies it has been written to
>>> satisfy the
>>> +requirements of the Displayport Link CTS Core 1.2 rev1.1 specification
>>> from VESA.
>>> +
>>> +Note that this application does not support eDP compliance testing.
>>> +
>>> +Compliance testing requires several components:
>>> +       - A kernel build that contains the patch set for DP compliance
>>> support
>>> +       - A Displayport compliance testing appliance such as the Unigraf
>>> DPR-100,
>>> +         DPR-120 or Quantum Data 882EDP
>>> +       - This user application
>>> +       - A Windows host machine to run the test software
>>> +       - Root access on the DUT
>>> +
>>> +Test setup:
>>> +       It is strongly recommended that the Windows host, test appliance
>>> and DUT be freshly
>>> +       restarted before testing begins. This ensures that any previous
>>> configurations and
>>> +       settings will not interfere with the test process. Refer to the
>>> test appliance
>>> +       documentation for setup, software installation and operation
>>> specific to that
>>> +       device.
>>> +
>>> +       The Linux DUT must be in text (console) mode and cannot have any
>>> other display
>>> +       manager running. The easiest way to accomplish this is to place
>>> the word “text”
>>> +       on the command line for booting the kernel. The recommended boot
>>> parameters for
>>> +       the kernel are as follows:
>>> +               “test debug drm.debug=0x0e”
>>> +       This enables debug output in the logs and will boot the system to
>>> a command prompt.
>>> +
>>> +       You must be logged in as root in order to run the user app.
>>> +
>>> +       Connections (required):
>>> +               - Test appliance connected to the external Displayport
>>> connector on the DUT
>>> +               - Test appliance connected to the Windows host (usually
>>> via USB)
>>> +
>>> +       Connections (optional):
>>> +               - SSH/telnet connection to the DUT from the Windows host
>>> or other system
>>> +               - A display attached to the DUT for monitoring the
>>> console or running
>>> +                 the user app.
>>> +
>>> +       The user app can be run directly from the console on the DUT (if
>>> a non-Displayport
>>> +       display is attached) or from an SSH/telnet session. Generally
>>> speaking, it’s best
>>> +       to run from an SSH session, but testing will operate identically
>>> regardless of how
>>> +       the user app is launched.
>>> +
>>> +       Once the user application is up and running, waiting for a test
>>> request, the software
>>> +       on the Windows host can now be used to execute the compliance
>>> tests.
>>> +
>>> +       TL;DR version:
>>> +               - Install test appliance software on Windows host
>>> +               - Adjust command line on Linux DUT as necessary to enable
>>> debug and boot
>>> +                 to the console
>>> +               - Connect the test appliance via USB to the Windows host
>>> and a Displayport
>>> +                 cable to the DUT
>>> +               - Reboot all systems, including the test appliance
>>> +               - SSH into the DUT or login at the command prompt
>>> +               - Ensure that the booted kernel contains the compliance
>>> test patch set
>>> +                 and that a version of IGT with this user app is also
>>> present
>>> +               - Launch the user app, as root. Follow the onscreen
>>> instructions until
>>> +                 it says it’s waiting for a test to begin
>>> +               - Execute the tests via the software on the Windows host
>>> per the
>>> +                 documentation for the test appliance
>>> +
>>> +Debugfs Files:
>>> +
>>> +The file root for all the debugfs files is:
>>> +
>>> +/sys/kernel/debug/dri/0/
>>> +
>>> +The specific files are as follows:
>>> +
>>> +i915_dp_test_active
>>> +       A simple flag that indicates whether or not compliance testing is
>>> currently active
>>> +       in the kernel. This flag is polled by userspace and once set,
>>> invokes the test
>>> +       handler in the user app.
>>> +
>>> +i915_dp_test_data
>>> +       Test data is used by the kernel to pass parameters to the user
>>> app. Currently, the
>>> +       only parameter that is delivered is the video mode to set for the
>>> test. As more tests
>>> +       are implemented, this value may be used for additional values or
>>> information.
>>> +
>>> +i915_dp_test_type
>>> +       The test type variable instructs the user app as to what the
>>> requested test was
>>> +       from the sink device. These values defined at the top of the
>>> application’s main
>>> +       implementation file and must be kept in sync with the values
>>> defined in
>>> +       drm_dp_helper.h.
>>> +
>>> +Test operations:
>>> +
>>> +Notes:
>>> +       If the test_active flag is stuck, it can be cleared with this
>>> simple command:
>>> +               echo 0 > /sys/kernel/debug/dri/0/i915_dp_test_active
>>> +       This can happen when the test appliance is connected and a test
>>> is run, but the
>>> +       user app isn’t running.
>>> +
>>> diff --git a/dp_compliance/build.sh b/dp_compliance/build.sh
>>> new file mode 100755
>>> index 0000000..0b317a9
>>> --- /dev/null
>>> +++ b/dp_compliance/build.sh
>>> @@ -0,0 +1,6 @@
>>> +#!/bin/sh
>>> +# Simple build script for the compliance app
>>> +# Eventually should be a makefile in IGT
>>
>> Yes, this should be a Makefile!
>>
>> Once common i-g-t flags are added there are a few minor compiler
>> warnings, mostly introduced by $(CWARNFLAGS).
>>
>> You'll also need to decide if you want to install the program by
>> default, and if so then including a man page would be useful.
>
> 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 at gmail.com>
>>> + *
>>> + * Elements of the modeset code adapted from David Herrmann's
>>> + * DRM modeset example
>>> + *
>>> +*/
>>> +
>>> +#include <stdio.h>
>>> +#include <stdlib.h>
>>> +#include <string.h>
>>> +#include <stdbool.h>
>>> +
>>> +#include <assert.h>
>>> +#include <drm.h>
>>> +#include <xf86drm.h>
>>> +#include <xf86drmMode.h>
>>> +#include <i915_drm.h>
>>> +
>>> +#include <unistd.h>
>>> +#include <sys/stat.h>
>>> +#include <signal.h>
>>> +#include <fcntl.h>
>>> +#include <errno.h>
>>> +#include <poll.h>
>>> +
>>> +#include <sys/mman.h>
>>> +#include <time.h>
>>> +
>>> +#define I915_DRIVER_NODE               "/dev/dri/card0"
>>> +#define INTEL_DP_DEBUGFS_ROOT          "/sys/kernel/debug/dri/0/"
>>> +
>>> +#define INTEL_DP_TEST_TYPE_FILE                "i915_dp_test_type"
>>> +#define INTEL_DP_TEST_ACTIVE_FILE      "i915_dp_test_active"
>>> +#define INTEL_DP_TEST_DATA_FILE                "i915_dp_test_data"
>>> +
>>> +/* DRM definitions - must be kept in sync with the DRM header */
>>> +#define DP_TEST_LINK_TRAINING          (1 << 0)
>>> +#define DP_TEST_LINK_VIDEO_PATTERN             (1 << 1)
>>> +#define DP_TEST_LINK_EDID_READ         (1 << 2)
>>> +#define DP_TEST_LINK_PHY_TEST_PATTERN          (1 << 3) /* DPCD >= 1.1
>>> */
>>> +
>>> +#define DP_COMPLIANCE_TEST_TYPE_MASK           (DP_TEST_LINK_TRAINING
>>> | \
>>> +                                        DP_TEST_LINK_VIDEO_PATTERN | \
>>> +                                        DP_TEST_LINK_EDID_READ     | \
>>> +                                        DP_TEST_LINK_PHY_TEST_PATTERN)
>>> +
>>> +/* NOTE: These must be kept in sync with the definitions in intel_dp.c
>>> */
>>> +#define INTEL_DP_EDID_SHIFT_MASK       0
>>> +#define INTEL_DP_EDID_OK               (0 << INTEL_DP_EDID_SHIFT_MASK)
>>> +#define INTEL_DP_EDID_CORRUPT          (1 << INTEL_DP_EDID_SHIFT_MASK)
>>> +#define INTEL_DP_RESOLUTION_SHIFT_MASK 4
>>> +#define INTEL_DP_RESOLUTION_PREFERRED  (1 <<
>>> INTEL_DP_RESOLUTION_SHIFT_MASK)
>>> +#define INTEL_DP_RESOLUTION_STANDARD   (2 <<
>>> INTEL_DP_RESOLUTION_SHIFT_MASK)
>>> +#define INTEL_DP_RESOLUTION_FAILSAFE   (3 <<
>>> INTEL_DP_RESOLUTION_SHIFT_MASK)
>>> +#define DP_COMPLIANCE_VIDEO_MODE_MASK   (INTEL_DP_RESOLUTION_PREFERRED |
>>> \
>>> +                                        INTEL_DP_RESOLUTION_STANDARD  |
>>> \
>>> +                                        INTEL_DP_RESOLUTION_FAILSAFE)
>>> +
>>> +enum
>>> +{
>>> +       INTEL_MODE_INVALID = -1,
>>> +       INTEL_MODE_NONE = 0,
>>> +       INTEL_MODE_PREFERRED,
>>> +       INTEL_MODE_STANDARD,
>>> +       INTEL_MODE_FAILSAFE
>>> +} intel_display_mode;
>>> +
>>> +struct dp_connector {
>>> +       drmModeModeInfo mode_standard, mode_preferred, mode_failsafe;
>>> +       /* Standard and preferred frame buffer*/
>>> +       uint32_t fb, fb_width, fb_height, fb_handle, fb_stride, fb_size;
>>> +       uint8_t *pixmap;
>>> +       /* Failsafe framebuffer - note this is a 16-bit buffer */
>>> +       uint32_t failsafe_fb, failsafe_width, failsafe_height;
>>> +       uint32_t failsafe_handle, failsafe_stride, failsafe_size;
>>> +       uint8_t *failsafe_pixmap;
>>> +
>>> +       uint32_t conn;
>>> +       uint32_t crtc;
>>> +
>>> +       drmModeCrtc *saved_crtc;
>>> +       struct dp_connector *next;
>>> +};
>>> +
>>> +/* Input handling from input.c */
>>> +extern void reset_terminal_mode(void);
>>> +extern void set_conio_terminal_mode(void);
>>> +extern int kbhit(void);
>>> +extern int getch(void);
>>> +
>>> +void setup_debugfs_files(int *test_active_fd, int *test_type_fd,
>>> +                        int *test_data_fd);
>>> + int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector
>>> *c,
>>> +                             struct dp_connector *dp_conn);
>>> + int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn);
>>> + int setup_failsafe_framebuffer(int drv_fd, struct dp_connector
>>> *dp_conn);
>>> + int setup_dp_connector(int drv_fd, drmModeRes *mr, drmModeConnector *c,
>>> +                       struct dp_connector *dp_conn);
>>> + int setup_connectors(int drv_fd, drmModeResPtr pmr);
>>> + int setup_drm(int *drv_fd, const char *node);
>>
>> Some of the lines around here appear to be intended by one space.
>
> 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 at 60 */
>>> +               if (c->modes[i].hdisplay == 800 &&
>>> +                   c->modes[i].vdisplay == 600 &&
>>> +                   c->modes[i].vrefresh == 60 &&
>>> +                   found_std == false) {
>>> +                       dp_conn->mode_standard = c->modes[i];
>>> +                       printf("\tStandard mode (%d) for connector %u is
>>> %ux%u\r\n",
>>> +                              i,
>>> +                              c->connector_id,
>>> +                              c->modes[i].hdisplay,
>>> +                              c->modes[i].vdisplay);
>>> +                       found_std = true;
>>> +               }
>>> +               /* Failsafe mode is 640x480 at 60 */
>>> +               if (c->modes[i].hdisplay == 640 &&
>>> +                   c->modes[i].vdisplay == 480 &&
>>> +                   c->modes[i].vrefresh == 60 &&
>>> +                   found_fs == false) {
>>> +                       dp_conn->mode_failsafe = c->modes[i];
>>> +                       dp_conn->failsafe_width = c->modes[i].hdisplay;
>>> +                       dp_conn->failsafe_height = c->modes[i].vdisplay;
>>> +                       printf("\tFailsafe mode (%d) for connector %u is
>>> %ux%u\r\n",
>>> +                              i,
>>> +                              c->connector_id,
>>> +                              c->modes[i].hdisplay,
>>> +                              c->modes[i].vdisplay);
>>> +               }
>>> +       }
>>> +
>>> +       ret = setup_crtc_for_connector(drv_fd, mr, c, dp_conn);
>>> +       if (ret) {
>>> +               printf("Set CRTC for connector %u failed (%d)\r\n",
>>> +                      c->connector_id, ret);
>>> +               return ret;
>>> +       }
>>> +
>>> +       ret = setup_framebuffers(drv_fd, dp_conn);
>>> +       if (ret) {
>>> +               printf("Create framebuffer for connector %u failed
>>> (%d)\r\n",
>>> +                      c->connector_id, ret);
>>> +               return ret;
>>> +       }
>>> +
>>> +       ret = setup_failsafe_framebuffer(drv_fd, dp_conn);
>>> +       if (ret) {
>>> +               printf("Create failsafe framebuffer for connector %u
>>> failed (%d)\r\n",
>>> +                      c->connector_id, ret);
>>> +               return ret;
>>> +       }
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +int setup_crtc_for_connector(int fd, drmModeRes *mr, drmModeConnector
>>> *c,
>>> +                            struct dp_connector *dp_conn)
>>> +{
>>> +       drmModeEncoder *drm_encoder = NULL;
>>> +       struct dp_connector *dpc;
>>> +       int i, j;
>>> +       int32_t crtc;
>>> +
>>> +       /* Use attached encoder if possible */
>>> +       if (c->encoder_id)
>>> +               drm_encoder = drmModeGetEncoder(fd, c->encoder_id);
>>> +
>>> +       if (drm_encoder) {
>>> +               if (drm_encoder->crtc_id) {
>>> +                       crtc = drm_encoder->crtc_id;
>>> +                       for (dpc = dp_connector_list; dpc; dpc =
>>> dpc->next) {
>>> +                               if (dpc->crtc == crtc) {
>>> +                                       crtc = -1;
>>> +                                       break;
>>> +                               }
>>> +                       }
>>> +                       if (crtc >= 0) {
>>> +                               drmModeFreeEncoder(drm_encoder);
>>> +                               dp_conn->crtc = crtc;
>>> +                               return 0;
>>> +                       }
>>> +               }
>>> +               drmModeFreeEncoder(drm_encoder);
>>> +       }
>>> +
>>> +       /* Check all encoder/crtc combinations */
>>> +       for (i = 0; i < c->count_encoders; ++i) {
>>> +               drm_encoder = drmModeGetEncoder(fd, c->encoders[i]);
>>> +               if (!drm_encoder) {
>>> +                       continue;
>>> +               }
>>> +               for (j = 0; j < mr->count_crtcs; ++j) {
>>> +                       if (!(drm_encoder->possible_crtcs & (1 << j)))
>>> +                               continue;
>>> +                       crtc = mr->crtcs[j];
>>> +                       for (dpc = dp_connector_list; dpc; dpc =
>>> dpc->next) {
>>> +                               if (dpc->crtc == crtc) {
>>> +                                       crtc = -1;
>>> +                                       break;
>>> +                               }
>>> +                       }
>>> +                       if (crtc >= 0) {
>>> +                               drmModeFreeEncoder(drm_encoder);
>>> +                               dp_conn->crtc = crtc;
>>> +                               return 0;
>>> +                       }
>>> +               }
>>> +               drmModeFreeEncoder(drm_encoder);
>>> +       }
>>> +       printf("No CRTC available for connector %u\r\n",
>>> c->connector_id);
>>> +       return -ENOENT;
>>> +}
>>> +
>>> +int setup_framebuffers(int drv_fd, struct dp_connector *dp_conn)
>>> +{
>>> +       struct drm_mode_create_dumb creq;
>>> +       struct drm_mode_destroy_dumb dreq;
>>> +       struct drm_mode_map_dumb mreq;
>>> +       int ret;
>>> +
>>> +       memset(&creq, 0, sizeof(creq));
>>> +       memset(&mreq, 0, sizeof(mreq));
>>> +
>>> +       creq.width = dp_conn->fb_width;
>>> +       creq.height = dp_conn->fb_height;
>>> +       creq.bpp = 32;
>>> +       ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
>>> +       if (ret < 0) {
>>> +               printf("Create dumb buffer failed\r\n");
>>> +               return -errno;
>>> +       }
>>> +       dp_conn->fb_stride = creq.pitch;
>>> +       dp_conn->fb_size = creq.size;
>>> +       dp_conn->fb_handle = creq.handle;
>>> +
>>> +       ret = drmModeAddFB(drv_fd, dp_conn->fb_width, dp_conn->fb_height,
>>> +                          24, 32, dp_conn->fb_stride,
>>> dp_conn->fb_handle,
>>> +                          &dp_conn->fb);
>>> +       if (ret) {
>>> +               printf("Create framebuffer failed\r\n");
>>> +               ret = -errno;
>>> +               goto cleanup;
>>> +       }
>>> +
>>> +       mreq.handle = dp_conn->fb_handle;
>>> +       ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
>>> +       if (ret) {
>>> +               printf("Map dumb buffer failed\r\n");
>>> +               ret = -errno;
>>> +               goto fb_fail;
>>> +       }
>>> +
>>> +       dp_conn->pixmap = mmap(0, dp_conn->fb_size, PROT_READ |
>>> PROT_WRITE,
>>> +                              MAP_SHARED, drv_fd, mreq.offset);
>>> +       if (dp_conn->pixmap == MAP_FAILED) {
>>> +               printf("Mmap failed\r\n");
>>> +               ret = -errno;
>>> +               goto fb_fail;
>>> +       }
>>> +       memset(dp_conn->pixmap, 0, dp_conn->fb_size);
>>> +       return 0;
>>> +
>>> +fb_fail:
>>> +       drmModeRmFB(drv_fd, dp_conn->fb);
>>> +
>>> +cleanup:
>>> +       memset(&dreq, 0, sizeof(dreq));
>>> +       dreq.handle = dp_conn->fb_handle;
>>> +       drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
>>> +       return ret;
>>> +}
>>> +
>>> +int setup_failsafe_framebuffer(int drv_fd, struct dp_connector *dp_conn)
>>> +{
>>> +       struct drm_mode_create_dumb creq;
>>> +       struct drm_mode_destroy_dumb dreq;
>>> +       struct drm_mode_map_dumb mreq;
>>> +       int ret;
>>> +
>>> +       memset(&creq, 0, sizeof(creq));
>>> +       creq.width = dp_conn->failsafe_width;
>>> +       creq.height = dp_conn->failsafe_height;
>>> +       creq.bpp = 16;
>>> +       ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq);
>>> +       if (ret < 0) {
>>> +               printf("Create dumb buffer failed\r\n");
>>> +               return -errno;
>>> +       }
>>> +
>>> +       dp_conn->failsafe_stride = creq.pitch;
>>> +       dp_conn->failsafe_size = creq.size;
>>> +       dp_conn->failsafe_handle = creq.handle;
>>> +
>>> +       ret = drmModeAddFB(drv_fd, dp_conn->failsafe_width,
>>> +                          dp_conn->failsafe_height, 16, 16,
>>> +                          dp_conn->failsafe_stride,
>>> dp_conn->failsafe_handle,
>>> +                          &dp_conn->failsafe_fb);
>>> +       if (ret) {
>>> +               printf("Create framebuffer failed\r\n");
>>> +               ret = -errno;
>>> +               goto cleanup;
>>> +       }
>>> +
>>> +       memset(&mreq, 0, sizeof(mreq));
>>> +       mreq.handle = dp_conn->failsafe_handle;
>>> +       ret = drmIoctl(drv_fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq);
>>> +       if (ret) {
>>> +               printf("Map dumb buffer failed\r\n");
>>> +               ret = -errno;
>>> +               goto fb_fail;
>>> +       }
>>> +
>>> +       dp_conn->failsafe_pixmap = mmap(0, dp_conn->failsafe_size,
>>> +                                       PROT_READ | PROT_WRITE,
>>> MAP_SHARED,
>>> +                                       drv_fd, mreq.offset);
>>> +       if (dp_conn->failsafe_pixmap == MAP_FAILED) {
>>> +               printf("cannot mmap dumb buffer (%d): %m\r\n", errno);
>>> +               ret = -errno;
>>> +               goto fb_fail;
>>> +       }
>>> +
>>> +       memset(dp_conn->failsafe_pixmap, 0, dp_conn->failsafe_size);
>>> +       return 0;
>>> +
>>> +fb_fail:
>>> +       drmModeRmFB(drv_fd, dp_conn->failsafe_fb);
>>> +
>>> +cleanup:
>>> +       memset(&dreq, 0, sizeof(dreq));
>>> +       dreq.handle = dp_conn->failsafe_handle;
>>> +       drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
>>> +       return ret;
>>> +}
>>> +
>>> +int set_video_mode(int drv_fd, int mode, struct dp_connector
>>> *test_connector)
>>> +{
>>> +       drmModeModeInfo *requested_mode;
>>> +       uint32_t required_fb;
>>> +       int ret = 0;
>>> +
>>> +       printf("Mode requested: ");
>>> +       switch (mode) {
>>> +       case INTEL_MODE_NONE:
>>> +               printf("NONE\r\n");
>>> +               ret = drmModeSetCrtc(drv_fd, test_connector->crtc,
>>> +                                    -1, 0, 0, NULL, 0, NULL);
>>> +               goto out;
>>> +               break;
>>> +       case INTEL_MODE_PREFERRED:
>>> +                printf("PREFERRED\r\n");
>>> +                requested_mode =  &test_connector->mode_preferred;
>>> +                required_fb = test_connector->fb;
>>> +                break;
>>> +        case INTEL_MODE_STANDARD:
>>> +                printf("STANDARD\r\n");
>>> +                requested_mode =  &test_connector->mode_standard;
>>> +                required_fb = test_connector->fb;
>>> +                break;
>>> +        case INTEL_MODE_FAILSAFE:
>>> +                printf("FAILSAFE\r\n");
>>> +                requested_mode =  &test_connector->mode_failsafe;
>>> +                required_fb = test_connector->failsafe_fb;
>>> +                break;
>>> +        case INTEL_MODE_INVALID:
>>> +        default:
>>> +                printf("INVALID! (%08x) Mode set aborted!\r\n", mode);
>>> +                return -1;
>>> +        break;
>>> +       }
>>> +       test_connector->saved_crtc = drmModeGetCrtc(drv_fd,
>>> test_connector->crtc);
>>> +       ret = drmModeSetCrtc(drv_fd, test_connector->crtc, required_fb,
>>> 0, 0,
>>> +                            &test_connector->conn, 1, requested_mode);
>>> +out:
>>> +       if (ret) {
>>> +               printf("Failed to set CRTC for connector %u\r\n",
>>> +                      test_connector->conn);
>>> +       }
>>> +       return ret;
>>> +}
>>> +
>>> +void shutdown(int drv_fd)
>>> +{
>>> +       int i;
>>> +       struct dp_connector *index = dp_connector_list, *prev;
>>> +       struct drm_mode_destroy_dumb dreq;
>>> +
>>> +       for (i = 0; i < dp_connector_count; i++) {
>>> +               if (index->saved_crtc) {
>>> +                       drmModeSetCrtc(drv_fd,
>>> index->saved_crtc->crtc_id,
>>> +                                      index->saved_crtc->buffer_id,
>>> +                                      index->saved_crtc->x,
>>> +                                      index->saved_crtc->y,
>>> &index->conn, 1,
>>> +                                      &index->saved_crtc->mode);
>>> +                       drmModeFreeCrtc(index->saved_crtc);
>>> +               }
>>> +               drmModeRmFB(drv_fd, index->fb);
>>> +               /* Clean up dumb buffer */
>>> +               memset(&dreq, 0, sizeof(dreq));
>>> +               dreq.handle = index->fb_handle;
>>> +               drmIoctl(drv_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq);
>>> +               prev = index;
>>> +               index = index->next;
>>> +               free(prev);
>>> +       }
>>> +}
>>> +
>>> +int main(int argc, char *argv[])
>>> +{
>>> +       int status = 0;
>>> +       int drv_fd = 0;
>>> +       drmModeResPtr mode_resources = NULL;
>>> +       int done = 0;
>>> +       char input;
>>> +       struct dp_connector *active_connector;
>>> +       int test_data, test_type;
>>> +
>>> +       /* Setup input for keyboard handling - from input.c */
>>> +       set_conio_terminal_mode();
>>> +
>>> +       printf("ATTENTION:\r\n"
>>> +              "   Ensure that no display manager is running\r\n"
>>> +              "   App must be run as root in console/text mode\r\n"
>>> +              "   Continue? (Y/N)"
>>> +              );
>>> +       input = getchar();
>>> +
>>> +       if (input != 'Y' && input != 'y')
>>> +               goto exit;
>>> +
>>> +       /* All-in-one setup function to get DRM off the ground */
>>> +       setup_drm(&drv_fd, I915_DRIVER_NODE);
>>> +
>>> +       /* From moderes, get the connectors, encoders, CRTCs, etc */
>>> +       setup_connectors(drv_fd, mode_resources);
>>> +
>>> +       active_connector = &dp_connector_list[0];
>>> +
>>> +       setup_debugfs_files(&test_active_fd, &test_type_fd,
>>> &test_data_fd);
>>> +
>>> +       printf("\r\n\r\nWaiting for test requests. 'X' or 'Q'
>>> exits\r\n");
>>
>> Would the standard ctrl-c and a signal handler suffice? This would
>> avoid using special terminal modes. Alternatively, there is also an
>> example of using the GLib mainloop in testdisplay and monitoring
>> stdin.
>
> 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.
>
>
>
>
>
>


More information about the Intel-gfx mailing list