All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jim Bride <jim.bride@linux.intel.com>
To: intel-gfx@lists.freedesktop.org
Cc: Jani Nikula <jani.nikula@intel.com>
Subject: [PATCH i-g-t] igt: Add intel_mst_decode utility
Date: Fri, 22 Apr 2016 15:57:14 -0700	[thread overview]
Message-ID: <1461365834-15221-1-git-send-email-jim.bride@linux.intel.com> (raw)

The intel_mst_decode utility parses the i915_dp_mst_info debugfs node
(either in-place or as a captured file via a command-line argument) and
prints information about the MST topology in an easy to read, hierarchical
format.  If a file is not specified on the command-line then the utility
will read directly from debugfs, and in this case root permissions are both
required and checked for.

cc: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Jim Bride <jim.bride@linux.intel.com>
---
 tools/Makefile.sources   |   3 +
 tools/intel_mst_decode.c | 528 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 531 insertions(+)
 create mode 100644 tools/intel_mst_decode.c

diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index 5d5958d..19f385f 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -25,6 +25,7 @@ bin_PROGRAMS = 				\
 	intel_infoframes		\
 	intel_l3_parity			\
 	intel_lid			\
+	intel_mst_decode		\
 	intel_opregion_decode		\
 	intel_panel_fitter		\
 	intel_perf_counters		\
@@ -57,3 +58,5 @@ intel_l3_parity_SOURCES =	\
 	intel_l3_parity.h	\
 	intel_l3_udev_listener.c
 
+intel_mst_decode_SOURCES =	\
+	intel_mst_decode.c
diff --git a/tools/intel_mst_decode.c b/tools/intel_mst_decode.c
new file mode 100644
index 0000000..f9dfd2d
--- /dev/null
+++ b/tools/intel_mst_decode.c
@@ -0,0 +1,528 @@
+/*
+ * i915 DisplayPort MST Topology Utility
+ * Print DP MST topology information for i915
+ *
+ * Copyright © 2016 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:
+ *     Jim Bride <jim.bride@linux.intel.com>
+ *
+ */
+#include "igt.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+/* Interesting constants and file locations */
+#define MAX_TIMESLOTS 64
+#define MAX_CHANNELS 3
+#define MAX_PORTS 8
+#define SINK_NAME_LEN 14
+#define DPCD_RCV_CAP_SIZE 15
+#define BRANCH_DEVID_SIZE 5
+#define SW_REV_SIZE 8
+#define HW_REV_SIZE SW_REV_SIZE
+#define FAUX_MST_SIZE 2
+#define TEXT_LABEL_SIZE 20
+#define PREFIX_SIZE 6
+#define LINE_BUFFER_SIZE 2048
+#define INTEL_DP_DEBUGFS_ROOT "/sys/kernel/debug/dri/0/"
+#define INTEL_DP_MST_INFO_FILE "i915_dp_mst_info"
+
+/* Command-line Parsing Stuff */
+const char *optstring = "af:hpx";
+static bool do_payload = false;
+static bool do_extra = false;
+static char *local_file = NULL;
+
+/* Data structures we populate from the MST info file. */
+struct mst_port {
+	bool is_valid;
+	bool ddps;
+	bool ldps;
+	bool input;
+	uint32_t peer_device_type;
+	uint32_t num_sdp_streams;
+	uint32_t num_sdp_connections;
+};
+
+struct vcpi_elem {
+	uint32_t state;
+	uint32_t port;
+	uint32_t start_slot;
+	uint32_t num_slots;
+	char sink_name[SINK_NAME_LEN];
+};
+
+struct payload_data {
+	struct vcpi_elem chan[MAX_CHANNELS]; // indexed by vcpi
+	uint8_t pay_table[MAX_TIMESLOTS];
+};
+
+struct primary_branch_info {
+	uint8_t dpcd_recv_cap[DPCD_RCV_CAP_SIZE];
+	uint8_t faux_mst[FAUX_MST_SIZE];
+	uint8_t mst_ctrl;
+	uint32_t branch_oui;
+	char devid[BRANCH_DEVID_SIZE];
+	char hw_rev[HW_REV_SIZE];
+	char sw_rev[SW_REV_SIZE];
+};
+
+struct mst_info {
+	int src_port;
+	struct mst_port port[MAX_PORTS];
+	struct payload_data paydata;
+	struct primary_branch_info pbi;
+};
+
+/* Prototypes to keep the compiler happy */
+
+static FILE *open_mst_info(void);
+static void close_mst_info(FILE *fp);
+static void parse_port_line(char *read_buf, struct mst_info *msti);
+static bool parse_mst_info(struct mst_info *msti);
+static void dump_mst_info(struct mst_info *msti);
+static void dump_extra(struct mst_info *msti);
+static void parse_vc_info(struct mst_info *msti, char *read_buf);
+static void parse_payload_info(struct mst_info *msti, char *read_buf);
+static void parse_pbi_info(struct mst_info *msti, char *read_buf, FILE *fp);
+static void parse_payload_table(struct mst_info *msti, char *read_buf);
+
+static FILE *open_mst_info(void)
+{
+	FILE *fp = NULL;
+
+	if (local_file) {
+		/* open local file */
+		fp = fopen(local_file, "r");
+		if (fp == NULL) {
+			fprintf(stderr, "Could not open local info file %s.\n",
+				local_file);
+		}
+	} else {
+		/* open sysfs file */
+		fp = igt_debugfs_fopen(INTEL_DP_MST_INFO_FILE, "r");
+		if (fp == NULL) {
+			fprintf(stderr, "Could not open sysfs file %s%s.\n",
+				INTEL_DP_DEBUGFS_ROOT, INTEL_DP_MST_INFO_FILE);
+		}
+	}
+	return fp;
+}
+
+static void close_mst_info(FILE *fp)
+{
+	if (!fp)
+		return;
+	fclose(fp);
+}
+
+static inline void get_byte_val(char **buf, int sep, uint8_t *bytes, int count)
+{
+	int i;
+
+	*buf = strchr(*buf, sep);
+	if (!*buf)
+		return;
+	*buf += 2; /* "<sep> " */
+	for (i = 0; i < count; i++, *buf += 3) {
+		sscanf(*buf, "%hhx", &(bytes[i]));
+	}
+
+}
+
+static inline void get_int_val(char **buf, int sep, int *val)
+{
+	*buf = strchr(*buf, sep);
+	*buf += 2; /* "<sep> " */
+	sscanf(*buf, "%d", val);
+}
+
+static void parse_port_line(char *read_buf, struct mst_info *msti)
+{
+
+	/* port: %d: input: %d: pdt: %d, ddps: %d ldps: %d, sdp: %d/%d, %x,
+	 * conn: %x || '(null)'
+	 */
+	char *idx = read_buf;
+	int cur_port = -1, val = -1;
+	struct mst_port *cp = NULL;
+
+	/* "port: %d: " */
+	get_int_val(&idx, ':', &cur_port);
+	idx += 2; /* ": " */
+	assert((cur_port > -1) && (cur_port < MAX_PORTS));
+
+	cp = &(msti->port[cur_port]);
+	/* "input: %d: " */
+	get_int_val(&idx, ':', &val);
+	cp->input = (val != 0) ? true : false;
+	idx += 2; /* ": " */
+
+	/* "pdt: %d, " */
+	get_int_val(&idx, ':', &val);
+	cp->peer_device_type = val;
+	idx += 2; /* ", " */
+
+	/* "ddps: %d ldps: %d, " */
+	get_int_val(&idx, ':', &val);
+	cp->ddps = val;
+	get_int_val(&idx, ':', &val);
+	cp->ldps = val;
+	idx += 2; /* ", " */
+
+	/* "sdp: %d/%d, " */
+	get_int_val(&idx, ':', &val);
+	cp->num_sdp_streams = val;
+	idx = strrchr(idx, '/');
+	if (!idx)
+		return;
+	sscanf(++idx, "%d", &val);
+	cp->num_sdp_connections = val;
+	/* Skip rest of line for now */
+
+	cp->is_valid = ((cp->ddps != 0) || (cp->ldps != 0));
+}
+
+static void parse_vc_info(struct mst_info *msti, char *read_buf)
+{
+	char *idx = read_buf;
+	struct vcpi_elem *cvc;
+	int val = -1;
+	int vc_chan;
+	char label[20];
+
+	/* "vcpi <index>" */
+	sscanf(idx, "%s %d", label, &vc_chan);
+
+	idx = strchr(read_buf, ':');
+	if (!idx)
+		return;
+	idx++; /* ':' */
+	cvc = &(msti->paydata.chan[vc_chan]);
+	if (strncmp(idx, "unused", 6) == 0)
+		return;
+
+	idx++; /* ' ' */
+	/* <port> <vcpi> <num_slots> "*/
+	sscanf(idx, "%d %d %d", &(cvc->port), &val, &(cvc->num_slots));
+
+	/* "sink name: (<sink_name>" || "Unknown") */
+	idx = strrchr(read_buf, ':');
+	if (!idx)
+		return;
+	idx += 2; /* ": " */
+	idx[strlen(idx) - 1] = '\0';
+	idx = strcpy(&(cvc->sink_name[0]), idx);
+	return;
+}
+
+static void parse_payload_info(struct mst_info *msti, char *read_buf)
+{
+	char *idx = read_buf;
+	char label[TEXT_LABEL_SIZE];
+	struct vcpi_elem *cvc;
+	int payload;
+
+	/* "payload <index>: " */
+	sscanf(idx, "%s %d", label, &payload);
+	idx += 2; /* ": " */
+	cvc = &(msti->paydata.chan[payload]);
+	/* "<payload_state>, " */
+	sscanf(idx, "%d", &(cvc->state));
+	idx = strchr(idx, ',');
+	idx += 2; /* ", " */
+	/* "<start_slot>, " */
+	sscanf(idx, "%d", &(cvc->start_slot));
+	idx = strchr(idx, ',');
+	idx += 2; /* ", " */
+	/* "<num_slots>" */
+	sscanf(idx, "%d", &(cvc->num_slots));
+}
+
+static void parse_pbi_info(struct mst_info *msti, char *read_buf, FILE *fp)
+{
+	char *idx;
+	char devid_label[TEXT_LABEL_SIZE];
+	char rev_label[TEXT_LABEL_SIZE];
+	char hw_label[TEXT_LABEL_SIZE];
+	char sw_label[TEXT_LABEL_SIZE];
+
+	/* "dpcd: <15 bytes>" */
+	idx = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!idx)
+		return;
+
+	/* "dpcd: " */
+	get_byte_val(&idx, ':', &(msti->pbi.dpcd_recv_cap[0]),
+		     DPCD_RCV_CAP_SIZE);
+
+	/* "faux/mst: <two bytes>" */
+	idx = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!idx)
+		return;
+	get_byte_val(&idx, ':', &(msti->pbi.faux_mst[0]), FAUX_MST_SIZE);
+
+	/* "mst ctrl: <byte>" */
+	idx = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!idx)
+		return;
+	get_byte_val(&idx, ':', &(msti->pbi.mst_ctrl), 1);
+
+	/* "branch oui: <3 bytes> devid: <4 chars> "
+	 * "revision: hw: 0xx.0xy sw: 0xx.0xy"
+	 */
+	idx = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!idx)
+		return;
+	idx = strchr(read_buf, ':');
+	idx += 2; /* ": " */
+	sscanf(idx, "%x %s %s %s %s %s %s %s", &(msti->pbi.branch_oui),
+	       devid_label, msti->pbi.devid, rev_label, hw_label,
+	       msti->pbi.hw_rev, sw_label, msti->pbi.sw_rev);
+}
+
+static void parse_payload_table(struct mst_info *msti, char *read_buf)
+{
+	char *idx = read_buf;
+
+	/* "payload table: <63 bytes>" */
+	get_byte_val(&idx, ':', &(msti->paydata.pay_table[0]),
+		     MAX_TIMESLOTS);
+}
+
+static bool parse_mst_info(struct mst_info *msti)
+{
+	char read_buf[LINE_BUFFER_SIZE];
+	char *s;
+	int pay_mask, vcpi_mask, total_vc;
+	int i;
+	FILE *fp = open_mst_info();;
+	bool bret = false;
+
+	if (!fp)
+		return false;
+
+	memset(read_buf, 0, LINE_BUFFER_SIZE);
+	memset(msti, 0, sizeof(struct mst_info));
+
+	/* Read which port we're gathering information about */
+	s =  fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!s)
+		goto out;
+
+	msti->src_port = tolower(read_buf[strlen(read_buf) - 2]) - 'a';
+
+	/* Strip off and ignore pointer info about the main MST object */
+	s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!s)
+		goto out;
+
+	/*
+	 * First parse out the port-related information.  Remember that
+	 * these will be the ports from the perspective of the primary
+	 * MST bridge device, and that they may or may not have anything
+	 * connected to them.
+	 */
+	s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (!s)
+		goto out;
+	/* parse the port lines until we get the first VCPI entry */
+	while (strncmp(read_buf, "vcpi", 4) != 0) {
+		parse_port_line(read_buf, msti);
+		s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+		if (!s)
+			goto out;
+	}
+
+	/* We have the first line of VCPI info when we get to this point.
+	 * This line tells us the mask to identify payloads, the mask to
+	 * identify virtual channels, and the maximum allowed number of
+	 * virtual channels.
+	 */
+	/* "vcpi: <payload_mask> <vcpi_mask>" <max_payloads> */
+	s = strchr(read_buf, ':');
+	if (!s)
+		goto out;
+	s += 2; /* strip ": " */
+	sscanf(s, "%d %d %d", &pay_mask, &vcpi_mask, &total_vc);
+	for (i = 0; i < total_vc; i++) {
+		s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+		if (!s)
+			goto out;
+		parse_vc_info(msti, read_buf);
+	}
+
+	for (i = 0; i < total_vc; i++) {
+		s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+		if (!s)
+			goto out;
+		parse_payload_info(msti, read_buf);
+	}
+
+	parse_pbi_info(msti, read_buf, fp);
+
+	s = fgets(read_buf, LINE_BUFFER_SIZE, fp);
+	if (s) {
+		parse_payload_table(msti, read_buf);
+		bret = true;
+	}
+
+out:
+	close_mst_info(fp);
+	return bret;
+}
+
+static void dump_extra(struct mst_info *msti)
+{
+	int i;
+
+	if (do_extra) {
+		printf("Primary Branch Info: \n");
+		printf("\tDPCD Receiver Caps: ");
+		for (i = 0; i < DPCD_RCV_CAP_SIZE; i++)
+			printf("%02hhx ", msti->pbi.dpcd_recv_cap[i]);
+		printf("\n");
+		printf("\tFaux MST: %02hhx %02hhx\n", msti->pbi.faux_mst[0],
+		       msti->pbi.faux_mst[1]);
+		printf("\tMST CTRL: 0x%02hhx\n", msti->pbi.mst_ctrl);
+		printf("\tBranch HW Version: %s SW Version: %s\n",
+		       msti->pbi.hw_rev, msti->pbi.sw_rev);
+		printf("\n");
+	}
+
+	if (do_payload) {
+		printf("Payload Table: ");
+		for (i = 0; i < MAX_TIMESLOTS; i++) {
+			if (i % 8 == 0)
+				printf("\n\t");
+			printf("%02hhx ", msti->paydata.pay_table[i]);
+		}
+		printf("\n");
+	}
+}
+
+static void dump_mst_info(struct mst_info *msti)
+{
+	int i, j;
+	int numBranches = 0;
+	char prefix[PREFIX_SIZE];
+
+	memset(prefix, 0, sizeof(prefix));
+	prefix[0] = '\t';
+	printf("Source MST Port %c.\n", 'A' + msti->src_port);
+
+	for (i = 0; i < MAX_PORTS; i++) {
+		if (msti->port[i].is_valid == false)
+			continue;
+		if ((msti->port[i].input) &&
+		    (msti->port[i].peer_device_type == 1)) {
+			printf("%s[Port %d] Branch Device %s (OUI %x)\n",
+			       prefix, i, msti->pbi.devid,
+			       msti->pbi.branch_oui);
+			printf("%s         [[ddps: %d ldps: %d sdp: %d/%d]]",
+			       prefix, msti->port[i].ddps, msti->port[i].ldps,
+			       msti->port[i].num_sdp_streams,
+			       msti->port[i].num_sdp_connections);
+			printf("\n\n");
+			prefix[++numBranches] = '\t';
+		} else if ((msti->port[i].input == 0) &&
+			   (msti->port[i].peer_device_type == 3)) {
+			const char *spaces = "         ";
+
+			/* Find and dump sinks associated with this port */
+			for (j = 0; j < MAX_CHANNELS; j++) {
+				if (msti->paydata.chan[j].port != i)
+					continue;
+
+				printf("%s[Port %d] Sink Device %s\n",
+				       prefix, i,
+				       msti->paydata.chan[j].sink_name);
+				printf("%s%s((VC: %d: start: %d: slots: %d))",
+				       prefix, spaces, j,
+				       msti->paydata.chan[j].start_slot,
+				       msti->paydata.chan[j].num_slots);
+				printf("\n");
+			}
+			printf("%s%s[[ddps: %d ldps: %d sdp: %d/%d]]",
+			       prefix, spaces, msti->port[i].ddps,
+			       msti->port[i].ldps,
+			       msti->port[i].num_sdp_streams,
+			       msti->port[i].num_sdp_connections);
+			printf("\n\n");
+		}
+	}
+	if ((do_extra) || (do_payload))
+		dump_extra(msti);
+}
+
+int main(int argc, char *argv[])
+{
+	int opt;
+	struct mst_info msti;
+	bool ret;
+
+	while ((opt = getopt(argc, argv, optstring)) != -1) {
+		switch (opt) {
+		case 'a':
+			do_payload = true;
+			do_extra = true;
+			break;
+		case 'f':
+			local_file = strdup(optarg);
+			break;
+		case 'h':
+			printf("Usage: %s [-a][-f <file>][-p][-x]\n",
+			       argv[0]);
+			printf("\t-a -- Print all info (-x + -p)\n");
+			printf("\t-f <file> -- Parse <file> rather than "
+			       "using sysfs.\n");
+			printf("\t-h -- Print help and exit.\n");
+			printf("\t-p -- Print payload table\n");
+			printf("\t-x -- Print extra configuration info.\n");
+			exit(EXIT_SUCCESS);
+		case 'p':
+			do_payload = true;
+			break;
+		case 'x':
+			do_extra = true;
+			break;
+		default:
+			fprintf(stderr, "Illegal option %c.\n", opt);
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if ((local_file == NULL) && (geteuid() != 0)) {
+		fprintf(stderr, "Must be root to run %s\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	ret = parse_mst_info(&msti);
+	if (ret)
+		dump_mst_info(&msti);
+	else
+		exit(EXIT_FAILURE);
+}
-- 
2.7.4

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

             reply	other threads:[~2016-04-22 22:57 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-22 22:57 Jim Bride [this message]
2016-05-14 15:50 ` [PATCH i-g-t] igt: Add intel_mst_decode utility Marius Vlad

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1461365834-15221-1-git-send-email-jim.bride@linux.intel.com \
    --to=jim.bride@linux.intel.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=jani.nikula@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.