[Intel-gfx] [PATCH i-g-t] igt: Add intel_mst_decode utility
Jim Bride
jim.bride at linux.intel.com
Fri Apr 22 22:57:14 UTC 2016
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 at intel.com>
Signed-off-by: Jim Bride <jim.bride at 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 at 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
More information about the Intel-gfx
mailing list