[igt-dev] [PATCH i-g-t v4 2/2] kms_content_protection: Add Content Protection test

Ramalingam C ramalingam.c at intel.com
Mon Oct 22 14:23:49 UTC 2018


Pretty simple test:
- picks the hdcp capable output with suitable pipe and apply modeset.
- checks the connected sink's hdcp capability through debugfs
- apply a FB and wait for the flip completion.
- clears the content protection property
- verifies that it clears
- sets the content protection property to desired
- verifies that it transitions to enabled
- incase of timeout three reattempts are implemented
- clear the content protection property and modeset on the crtc

Above steps are repeated on all HDCP capable connectors for both
legacy and atomic subtests.

v2:
  dynamic subtests are dropped [Daniel]
v3:
  debugfs is used to detect the sink's hdcp capability [Daniel]
  data structure is made as global variable.
v4:
  debugfs file from connector's debugfs dir is used [Daniel]

Signed-off-by: Sean Paul <seanpaul at chromium.org>
Signed-off-by: Ramalingam C <ramalingam.c at intel.com>
---
 lib/igt_kms.c                  |   1 +
 lib/igt_kms.h                  |   1 +
 tests/Makefile.sources         |   1 +
 tests/kms_content_protection.c | 301 +++++++++++++++++++++++++++++++++++++++++
 tests/meson.build              |   1 +
 5 files changed, 305 insertions(+)
 create mode 100644 tests/kms_content_protection.c

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 62d8468415c6..4231996c65ca 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -194,6 +194,7 @@ const char * const igt_connector_prop_names[IGT_NUM_CONNECTOR_PROPS] = {
 	[IGT_CONNECTOR_CRTC_ID] = "CRTC_ID",
 	[IGT_CONNECTOR_DPMS] = "DPMS",
 	[IGT_CONNECTOR_BROADCAST_RGB] = "Broadcast RGB",
+	[IGT_CONNECTOR_CONTENT_PROTECTION] = "Content Protection",
 };
 
 /*
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 3a12f2782eed..aa068e58e607 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -120,6 +120,7 @@ enum igt_atomic_connector_properties {
        IGT_CONNECTOR_CRTC_ID,
        IGT_CONNECTOR_DPMS,
        IGT_CONNECTOR_BROADCAST_RGB,
+       IGT_CONNECTOR_CONTENT_PROTECTION,
        IGT_NUM_CONNECTOR_PROPS
 };
 
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index c84933f1d971..5af68a61df51 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -174,6 +174,7 @@ TESTS_progs = \
 	kms_chv_cursor_fail \
 	kms_color \
 	kms_concurrent \
+	kms_content_protection\
 	kms_crtc_background_color \
 	kms_cursor_crc \
 	kms_cursor_legacy \
diff --git a/tests/kms_content_protection.c b/tests/kms_content_protection.c
new file mode 100644
index 000000000000..e8de7d359d09
--- /dev/null
+++ b/tests/kms_content_protection.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright © 2018 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.
+ *
+ */
+
+#include <poll.h>
+#include "igt.h"
+#include "igt_sysfs.h"
+
+IGT_TEST_DESCRIPTION("Test content protection (HDCP)");
+
+struct data {
+	int drm_fd;
+	igt_display_t display;
+} data;
+
+
+static void flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+			 unsigned int tv_usec, void *_data)
+{
+	igt_debug("Flip event received.\n");
+}
+
+static int wait_flip_event(void)
+{
+	int rc;
+	drmEventContext evctx;
+	struct pollfd pfd;
+
+	evctx.version = 2;
+	evctx.vblank_handler = NULL;
+	evctx.page_flip_handler = flip_handler;
+
+	pfd.fd = data.drm_fd;
+	pfd.events = POLLIN;
+	pfd.revents = 0;
+
+	rc = poll(&pfd, 1, 1000);
+	switch (rc) {
+	case 0:
+		igt_info("Poll timeout. 1Sec.\n");
+		rc = -ETIMEDOUT;
+		break;
+	case 1:
+		rc = drmHandleEvent(data.drm_fd, &evctx);
+		igt_assert_eq(rc, 0);
+		rc = 0;
+		break;
+	default:
+		igt_info("Unexpected poll rc %d\n", rc);
+		rc = -1;
+		break;
+	}
+
+	return rc;
+}
+
+static bool
+wait_for_prop_value(igt_output_t *output, uint64_t expected,
+		    uint32_t timeout_mSec)
+{
+	uint64_t val;
+	int i;
+
+	for (i = 0; i < timeout_mSec; i++) {
+		val = igt_output_get_prop(output,
+					  IGT_CONNECTOR_CONTENT_PROTECTION);
+		if (val == expected)
+			return true;
+		usleep(1000);
+	}
+	igt_info("prop_value mismatch %ld != %ld\n", val, expected);
+
+	return false;
+}
+
+static void
+commit_display_and_wait_for_flip(enum igt_commit_style s)
+{
+	int ret;
+	uint32_t flag;
+
+	if (s == COMMIT_ATOMIC) {
+		flag = DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET;
+		igt_display_commit_atomic(&data.display, flag, NULL);
+
+		ret = wait_flip_event();
+		igt_assert_f(!ret, "wait_flip_event failed. %d\n", ret);
+	} else {
+		igt_display_commit2(&data.display, s);
+
+		/* Wait for 50mSec */
+		usleep(50 * 1000);
+	}
+}
+
+static void
+test_cp_enable_disable(const enum pipe pipe, igt_output_t *output,
+		       enum igt_commit_style s)
+{
+	igt_display_t *display = &data.display;
+	drmModeModeInfo mode;
+	igt_plane_t *primary;
+	struct igt_fb red, green;
+	bool ret;
+	int retry = 3;
+
+	igt_assert(kmstest_get_connector_default_mode(
+			display->drm_fd, output->config.connector, &mode));
+
+	igt_output_override_mode(output, &mode);
+	igt_output_set_pipe(output, pipe);
+
+	igt_create_color_fb(display->drm_fd, mode.hdisplay, mode.vdisplay,
+			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
+			    1.f, 0.f, 0.f, &red);
+	igt_create_color_fb(display->drm_fd, mode.hdisplay, mode.vdisplay,
+			    DRM_FORMAT_XRGB8888, LOCAL_DRM_FORMAT_MOD_NONE,
+			    0.f, 1.f, 0.f, &green);
+
+	primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	igt_display_commit2(display, s);
+
+	igt_plane_set_fb(primary, &red);
+
+	/* Wait for Flip completion before starting the HDCP authentication */
+	commit_display_and_wait_for_flip(s);
+
+	do {
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_CONTENT_PROTECTION, 0);
+		igt_display_commit2(display, s);
+
+		/* Wait for HDCP to be disabled for fresh start. */
+		ret = wait_for_prop_value(output, 0, 1000);
+		igt_assert_f(ret, "Content Protection not cleared\n");
+
+		igt_output_set_prop_value(output,
+					  IGT_CONNECTOR_CONTENT_PROTECTION, 1);
+		igt_display_commit2(display, s);
+
+		/* Wait for 18000mSec (3 authentication * 6Sec) */
+		ret = wait_for_prop_value(output, 2, 18000);
+		if (ret) {
+			igt_plane_set_fb(primary, &green);
+			igt_display_commit2(display, s);
+		}
+
+		if (!ret && --retry)
+			igt_debug("Retry (%d/2) ...\n", 3 - retry);
+	} while (retry && !ret);
+
+	igt_assert_f(ret, "Content Protection not enabled\n");
+
+	igt_output_set_prop_value(output, IGT_CONNECTOR_CONTENT_PROTECTION, 0);
+	igt_plane_set_fb(primary, &red);
+	igt_display_commit2(display, s);
+
+	/* Wait for HDCP to be disabled, before crtc off */
+	wait_for_prop_value(output, 0, 1000);
+
+	igt_plane_set_fb(primary, NULL);
+	igt_output_set_pipe(output, PIPE_NONE);
+}
+
+static bool igt_pipe_is_free(igt_display_t *display, enum pipe pipe)
+{
+	int i;
+
+	for (i = 0; i < display->n_outputs; i++)
+		if (display->outputs[i].pending_pipe == pipe)
+			return false;
+
+	return true;
+}
+
+static void
+test_content_protection_on_output(igt_output_t *output,
+				  enum igt_commit_style s)
+{
+	igt_display_t *display = &data.display;
+	enum pipe pipe;
+
+	for_each_pipe(display, pipe) {
+		if (!igt_pipe_connector_valid(pipe, output))
+			continue;
+
+		/*
+		 * If previous subtest of connector failed, pipe
+		 * attached to that connector is not released.
+		 * Because of that we have to choose the non
+		 * attached pipe for this subtest.
+		 */
+		if (!igt_pipe_is_free(display, pipe))
+			continue;
+
+		test_cp_enable_disable(pipe, output, s);
+
+		/*
+		 * Testing a output with a pipe is enough for HDCP
+		 * testing. No ROI in testing the connector with other
+		 * pipes. So Break the loop on pipe.
+		 */
+		break;
+	}
+
+}
+
+static void __debugfs_read(int fd, const char *param, char *buf, int len)
+{
+	len = igt_debugfs_simple_read(fd, param, buf, len);
+	if (len < 0)
+		igt_assert_eq(len, -ENODEV);
+}
+
+#define debugfs_read(fd, p, arr) __debugfs_read(fd, p, arr, sizeof(arr))
+
+#define MAX_SINK_HDCP_CAP_BUF_LEN	500
+static bool sink_hdcp_capable(igt_output_t *output)
+{
+	char buf[MAX_SINK_HDCP_CAP_BUF_LEN];
+	int fd;
+
+	fd = igt_debugfs_connector_dir(data.drm_fd, output->name);
+	if (fd < 0)
+		return false;
+
+	debugfs_read(fd, "i915_hdcp_sink_capability", buf);
+	close(fd);
+
+	igt_debug("Sink capability: %s\n", buf);
+
+	return strstr(buf, "HDCP1.4");
+}
+
+
+static void
+test_content_protection(enum igt_commit_style s)
+{
+	igt_display_t *display = &data.display;
+	igt_output_t *output;
+	int valid_tests = 0;
+
+	for_each_connected_output(display, output) {
+		if (!output->props[IGT_CONNECTOR_CONTENT_PROTECTION])
+			continue;
+
+		igt_info("CP Test execution on %s\n", output->name);
+		if (!sink_hdcp_capable(output)) {
+			igt_info("\tSkip %s (Sink has no HDCP support)\n",
+				 output->name);
+			continue;
+		}
+
+		test_content_protection_on_output(output, s);
+		valid_tests++;
+	}
+
+	igt_require_f(valid_tests, "No connector found with HDCP capability\n");
+}
+
+igt_main
+{
+	igt_fixture {
+		igt_skip_on_simulation();
+
+		data.drm_fd = drm_open_driver(DRIVER_ANY);
+
+		igt_display_init(&data.display, data.drm_fd);
+	}
+
+	igt_subtest("legacy")
+		test_content_protection(COMMIT_LEGACY);
+
+	igt_subtest("atomic") {
+		igt_require(data.display.is_atomic);
+		test_content_protection(COMMIT_ATOMIC);
+	}
+
+	igt_fixture
+		igt_display_fini(&data.display);
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17deb945ec95..a74de4ff0ece 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -149,6 +149,7 @@ test_progs = [
 	'kms_chv_cursor_fail',
 	'kms_color',
 	'kms_concurrent',
+	'kms_content_protection',
 	'kms_crtc_background_color',
 	'kms_cursor_crc',
 	'kms_cursor_legacy',
-- 
2.7.4



More information about the igt-dev mailing list