Re: [igt-dev] [PATCH i-g-t] tools/dpcd_reg: Add dump all functionality

Tarun Vyas
2018-10-30T03:47:34Z

On Tue, Oct 23, 2018 at 12:13:34PM +0000, Tarun Vyas wrote:
> On Tue, Oct 23, 2018 at 11:50:47AM +0300, Jani Nikula wrote:
> > On Mon, 22 Oct 2018, Tarun Vyas <tarun.vyas@intel.com> wrote:
> > > From: Rodrigo Vivi <rodrigo.vivi@intel.com>
> > >
> > > And make it the default when no operation or device is given.
> > >
> > > So, this will replace i915_dpcd for now but can be expanded
> > > later.
> > >
> > > Current debugfs:
> > >
> > > $ sudo cat /sys/kernel/debug/dri/0/DP-1/i915_dpcd
> > > 0000: 12 14 c4 01 01 00 01 00 02 02 06 00 00 00 02
> > > 0070: 00 00
> > > 0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> > > 0100: 14 84 00 06 06 06 06 00 01 04 00
> > > 0200: 41 00 77 77 01 03 22 22
> > > 0600: 01
> > > 0700: 01
> > > 0701: 00 00 00 00
> > > 0720: 00 01 00 00 00 01 01 00 00 00 00 00 01 00 00 01
> > > 0732: 00 00
> > >
> > > $ sudo cat /sys/kernel/debug/dri/0/eDP-1/i915_dpcd
> > > 0000: 12 14 84 40 00 00 01 01 02 00 00 00 00 0b 00
> > > 0070: 01 00
> > > 0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> > > 0100: 14 04 00 00 00 00 00 00 00 00 00
> > > 0200: 41 00 00 00 80 00 66 66
> > > 0600: 01
> > > 0700: 02
> > > 0701: 9f 40 00 00
> > > 0720: 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
> > > 0732: 00 00
> > >
> > > new tool:
> > >
> > > $ sudo tools/dpcd_reg
> > >
> > > Dumping DPDDC-B
> > > 0x0000: 12 14 c4 01 01 00 01 00 02 02 06 00 00 00 02
> > > 0x0070: 00 00
> > > 0x0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> > > 0x0100: 14 84 00 06 06 06 06 00 01 04 00
> > > 0x0200: 41 00 77 77 01 03 22 22
> > > 0x0600: 01
> > > 0x0700: 01
> > > 0x0701: 00 00 00 00
> > > 0x0720: 00 01 00 00 00 01 01 00 00 00 00 00 01 00 00 01
> > > 0x0732: 00 00
> > > 0x2008: 00
> > >
> > > Dumping DPDDC-A
> > > 0x0000: 14 0a 82 41 00 00 01 c0 02 00 00 00 0f 09 80
> > > 0x0070: 03 1b
> > > 0x0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> > > 0x0100: 00 82 00 00 00 00 00 00 01 08 00
> > > 0x0200: 01 00 00 00 00 01 00 00
> > > 0x0600: 01
> > > 0x0700: 04
> > > 0x0701: fb ff 00 00
> > > 0x0720: 01 03 ff ff 10 0a 10 00 01 00 01 00 00 c0 00 00
> > > 0x0732: 00 14
> > > 0x2008: 02
> > >
> > > Cc: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com>
> > > Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
> > > Signed-off-by: Tarun Vyas <tarun.vyas@intel.com>
> > > ---
> > > tools/dpcd_reg.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
> > > 1 file changed, 91 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/tools/dpcd_reg.c b/tools/dpcd_reg.c
> > > index 2761168d..51cec85f 100644
> > > --- a/tools/dpcd_reg.c
> > > +++ b/tools/dpcd_reg.c
> > > @@ -35,6 +35,7 @@
> > > #include <unistd.h>
> > > #include <limits.h>
> > > #include <stdbool.h>
> > > +#include <dirent.h>
> > >
> > > #define MAX_DP_OFFSET 0xfffff
> > > #define DRM_AUX_MINORS 256
> > > @@ -109,6 +110,35 @@ static inline bool strtol_err_util(char *endptr, long *val)
> > > return false;
> > > }
> > >
> > > +static void connector_name(char *f_name, char *dpcd_name)
> > > +{
> > > + char sysfs_path[PATH_MAX];
> > > + int sysfs_fd, ret;
> > > +
> > > + snprintf(sysfs_path, PATH_MAX,
> > > + "/sys/class/drm_dp_aux_dev/%s/name",
> > > + f_name);
> > > + sysfs_fd = open(sysfs_path, O_RDONLY);
> > > + if (sysfs_fd < 0) {
> > > + fprintf(stderr,
> > > + "fail to open %s\n", sysfs_path);
> > > + return;
> > > + }
> > > +
> > > + ret = read(sysfs_fd, dpcd_name, sizeof(dpcd_name) - 1);
> >
> > Serious bug #1. The sizeof doesn't do what you intend.
> >
> Yes, that was stupid. The original patch was doing it right where the array was declared. I messed it up while creating a new function out of it.
> > Regular C bug. read() may return a positive result smaller than
> > requested. See readN() in igt_sysfs.c.
> >
> >
> Yes, but, if the read was partial (or zero) , then we would still like to treat it as a failure, in this specific case, right ?
> > > + if (ret < 0) {
> > > + fprintf(stderr,
> > > + "fail to read dpcd name from %s\n\n", sysfs_path);
> >
> > Two newlines is unnecessary.
> >
> Ok.
> > > + goto cleanup;
> > > + }
> > > +
> > > + dpcd_name[ret] = '\0';
> > > + printf("%s\n", dpcd_name);
> >
> > Why does this function both retrieve the name to the caller and print
> > it? Those are two things that should stay orthogonal.
> >
> I kept them separate originally...will work on it in the next version.
> > > +
> > > +cleanup:
> > > + close(sysfs_fd);
> > > +}
> > > +
> > > static int parse_opts(struct dpcd_data *dpcd, int argc, char **argv)
> > > {
> > > int ret, vflag = 0;
> > > @@ -280,13 +310,61 @@ static int dpcd_dump(int fd)
> > > return ret;
> > > }
> > >
> > > +static int dpcd_dump_all(void)
> > > +{
> > > + struct dirent *dirent;
> > > + DIR *dir;
> > > + char dev_path[PATH_MAX];
> > > + int dev_fd, ret = 0;
> > > + char dpcd_name[8];
> > > + char discard;
> > > +
> > > + dir = opendir("/sys/class/drm_dp_aux_dev/");
> > > + if (!dir) {
> > > + fprintf(stderr, "fail to read /dev\n");
> > > + return ENOENT;
> > > + }
> > > +
> > > + while ((dirent = readdir(dir))) {
> > > + if (strncmp(dirent->d_name, "drm_dp_aux", 10) == 0) {
> > > + snprintf(dev_path, PATH_MAX,
> > > + "/dev/%s", dirent->d_name);
> > > +
> > > + dev_fd = open(dev_path, O_RDONLY);
> > > + if (dev_fd < 0) {
> > > + fprintf(stderr,
> > > + "fail to open %s\n", dev_path);
> > > + continue;
> > > + }
> > > +
> > > + printf("\nDumping ");
> > > + connector_name(dirent->d_name, dpcd_name);
> >
> > And what if connector_name() fails?
> >
> I had connector_name() return an int but then changed it to void b/c I was like,
> I'd still want to dump the registers regardless of connector_name.
> Do we wanna skip it all if we cant get the connector_name ?
> > > +
> > > + /* Dummy read to check if dpcd is available */
> > > + ret = read(dev_fd, &discard, 1);
> >
> > Serious bug #2. This read botches up the whole dump.
> >
> Hmm. we should skip the dump only if the read return a -ve...
> > > + if (ret != 1) {
> > > + printf("DPCD %s seems not available. skipping...\n",
> > > + dpcd_name);
> > > + continue;
> > > + }
> > > +
> > > + dpcd_dump(dev_fd);
> > > + close(dev_fd);
> > > + }
> > > + }
> > > +
> > > + closedir(dir);
> > > +
> > > + return EXIT_SUCCESS;
> > > +}
> > > int main(int argc, char **argv)
> > > {
> > > char dev_name[20];
> > > + char sys_name[16], dpcd_name[8];
> > > int ret, fd;
> > >
> > > struct dpcd_data dpcd = {
> > > - .devid = 0,
> > > + .devid = -1,
> > > .file_op = O_RDONLY,
> > > .rw.offset = 0x0,
> > > .rw.count = 1,
> > > @@ -297,7 +375,16 @@ int main(int argc, char **argv)
> > > if (ret != EXIT_SUCCESS)
> > > return ret;
> > >
> > > + if (dpcd.devid < 0) {
> > > + if (dpcd.cmd == DUMP)
> > > + return dpcd_dump_all();
> > > +
> > > + dpcd.devid = 0;
> > > + }
> > > +
> > > snprintf(dev_name, strlen(aux_dev) + 4, "%s%d", aux_dev, dpcd.devid);
> >
> > That's not part of this patch, but what on earth does strlen(aux_dev)
> > have to do with how much fits into dev_name?!?
> >
> Yes. Will fix it.
> > > + snprintf(sys_name, 16, "drm_dp_aux%d", dpcd.devid);
> >
> > sizeof.
> >
> Right
> > > +
> >
> > Superfluous newline.
> >
> Ok
> > >
> > > fd = open(dev_name, dpcd.file_op);
> > > if (fd < 0) {
> > > @@ -307,6 +394,9 @@ int main(int argc, char **argv)
> > > return errno;
> > > }
> > >
> > > + printf("\n");
> > > + connector_name(sys_name, dpcd_name);
> > > +
> >
> > Why is the output different for one vs. all devices dumping? Dumping one
> > specified device should look exactly the same as dumping all devices
> > when there's just one available device. Maybe this shouldn't be
> > duplicated. Maybe the dump path shouldn't be in *any* way different for
> > multiple vs one device. Maybe you could write a dump wrapper that takes
> > the aux dev name pattern as input, and in the specified case it should
> > only match one. Would make the whole thing much neater.
c> >
> > BR,
> > Jani.
> >
Another thing is that dpcd_dump_all() iterates over all of the available devices,
whereas for a single device dump, we specify the device id itself.

Here is the diff for unification of the dump path:

diff --git a/tools/dpcd_reg.c b/tools/dpcd_reg.c
index 51cec85f..fa7f077a 100644
--- a/tools/dpcd_reg.c
+++ b/tools/dpcd_reg.c
@@ -110,7 +110,7 @@ static inline bool strtol_err_util(char *endptr, long *val)
return false;
}

-static void connector_name(char *f_name, char *dpcd_name)
+static void connector_name(char *f_name, char *dpcd_name, size_t size)
{
char sysfs_path[PATH_MAX];
int sysfs_fd, ret;
@@ -125,15 +125,14 @@ static void connector_name(char *f_name, char *dpcd_name)
return;
}

- ret = read(sysfs_fd, dpcd_name, sizeof(dpcd_name) - 1);
+ ret = read(sysfs_fd, dpcd_name, size - 1);
if (ret < 0) {
fprintf(stderr,
"fail to read dpcd name from %s\n\n", sysfs_path);
goto cleanup;
}

- dpcd_name[ret] = '\0';
- printf("%s\n", dpcd_name);
+ dpcd_name[size - 1] = '\0';

cleanup:
close(sysfs_fd);
@@ -310,15 +309,21 @@ static int dpcd_dump(int fd)
return ret;
}

-static int dpcd_dump_all(void)
+static int dpcd_dump_all(int devid)
{
struct dirent *dirent;
DIR *dir;
char dev_path[PATH_MAX];
int dev_fd, ret = 0;
char dpcd_name[8];
+ char dev_pattern[16];
char discard;

+ if (devid < 0)
+ strcpy(dev_pattern, "drm_dp_aux");
+ else
+ snprintf(dev_pattern, sizeof(dev_pattern), "drm_dp_aux%d", devid);
+
dir = opendir("/sys/class/drm_dp_aux_dev/");
if (!dir) {
fprintf(stderr, "fail to read /dev\n");
@@ -326,7 +331,7 @@ static int dpcd_dump_all(void)
}

while ((dirent = readdir(dir))) {
- if (strncmp(dirent->d_name, "drm_dp_aux", 10) == 0) {
+ if (strncmp(dirent->d_name, dev_pattern, strlen(dev_pattern)) == 0) {
snprintf(dev_path, PATH_MAX,
"/dev/%s", dirent->d_name);

@@ -337,17 +342,18 @@ static int dpcd_dump_all(void)
continue;
}

- printf("\nDumping ");
- connector_name(dirent->d_name, dpcd_name);
+ connector_name(dirent->d_name, dpcd_name,
+ sizeof(dpcd_name));

/* Dummy read to check if dpcd is available */
ret = read(dev_fd, &discard, 1);
- if (ret != 1) {
+ if (ret < 0) {
printf("DPCD %s seems not available. skipping...\n",
dpcd_name);
continue;
}

+ printf("\nDumping %s\n", dpcd_name);
dpcd_dump(dev_fd);
close(dev_fd);
}
@@ -375,15 +381,14 @@ int main(int argc, char **argv)
if (ret != EXIT_SUCCESS)
return ret;

- if (dpcd.devid < 0) {
- if (dpcd.cmd == DUMP)
- return dpcd_dump_all();
-
+ if (dpcd.cmd == DUMP)
+ return dpcd_dump_all(dpcd.devid);
+ else if (dpcd.devid < 0)
dpcd.devid = 0;
- }

- snprintf(dev_name, strlen(aux_dev) + 4, "%s%d", aux_dev, dpcd.devid);
- snprintf(sys_name, 16, "drm_dp_aux%d", dpcd.devid);
+
+ snprintf(dev_name, sizeof(dev_name), "%s%d", aux_dev, dpcd.devid);
+ snprintf(sys_name, sizeof(sys_name), "drm_dp_aux%d", dpcd.devid);

fd = open(dev_name, dpcd.file_op);
@@ -394,8 +399,8 @@ int main(int argc, char **argv)
return errno;
}

- printf("\n");
- connector_name(sys_name, dpcd_name);
+ connector_name(sys_name, dpcd_name, sizeof(dpcd_name));
+ printf("\n%s\n", dpcd_name);

switch (dpcd.cmd) {
case READ:
@@ -404,9 +409,8 @@ int main(int argc, char **argv)
case WRITE:
ret = dpcd_write(fd, dpcd.rw.offset, dpcd.val);
break;
- case DUMP:
default:
- ret = dpcd_dump(fd);
+ /* No-op to make the compiler happy */
break;
}

Thanks,
Tarun
> My thought was that at this point, it wont be a dump anymore.
> I meant this specifically for reads/writes of byte(s), to a specific device.
> That's why I changed the connector_name() to include printing the dpcd_name
> (but that's just not right). But, yea, the idea was to make this look different
> than a regular dump of all registers, b/c here we just want to print the connector
> name for the device we are looking at.
>
> Thanks for the review Jani.
>
> - Tarun
> > > switch (dpcd.cmd) {
> > > case READ:
> > > ret = dpcd_read(fd, dpcd.rw.offset, dpcd.rw.count);
> >
> > --
> > Jani Nikula, Intel Open Source Graphics Center