Message ID | 1461365834-15221-1-git-send-email-jim.bride@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Could you also add a man page for it? On Fri, Apr 22, 2016 at 03:57:14PM -0700, Jim Bride wrote: > 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); Albeit redundant, but maybe strncpy()? > + return; Not needed. > +} > + > +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); You might want to use '%*s' so you will not need all those arrays defined locally. You have other instances of this. > +} > + > +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'; Why '-2'? -- just add a comment with the format so its clear... > + > + /* 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 = " "; Why not use '\t\s' in printf() and remove this? > + > + /* 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); return 0; ? > +} > -- > 2.7.4 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/intel-gfx
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); +}
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