Initial distribution of NVIDIA's power query tool.

Signed-off-by: Anton Staaf <robotboy@chromium.org>

BUG=chrome-os-partner:2381
TEST=run make successfully
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cfe68e3
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=-std=c99 -Wall -O2
+
+tegra-power-query: power_tools.c seaboard.h power_tools.h
+	$(CC) $(CFLAGS) -o $@ $^
+
+clean:
+	rm tegra-power-query
+
+.PHONY: clean
diff --git a/README b/README
new file mode 100644
index 0000000..4525744
--- /dev/null
+++ b/README
@@ -0,0 +1,7 @@
+Build with 'make'.
+Run with './tegra-power-query'.
+For help, './tegra-power-query -h'.
+
+If you're having trouble, make sure that you have CONFIG_USB_HIDDEV enabled in
+your kernel and that you have read access to the device nodes at
+"/dev/usb/hiddev*".
diff --git a/plot/plot_all.sh b/plot/plot_all.sh
new file mode 100755
index 0000000..5bde934
--- /dev/null
+++ b/plot/plot_all.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+#
+# Example of how to draw a couple of windows displaying all of the captured
+# data in real-time.
+#
+# This uses the "driveGnuPlotStreams.pl" available from
+# http://users.softlab.ntua.gr/~ttsiod/gnuplotStreaming.html
+#
+# At least on my computer, this isn't particularly fast; limiting it to a
+# subset of the data is much faster.
+#
+
+
+../tegra-power-query -s | ./driveGnuPlotStreams.pl \
+    16 \
+    2 \
+    500 500 \
+    0 0.3 \
+    0 16 \
+    500x500+0+30 \
+    500x500+510+30 \
+    "BAT Voltage" \
+    "BAT Current" \
+    "SYS_SM1 Voltage" \
+    "SYS_SM1 Current" \
+    "SYS_SM2 Voltage" \
+    "SYS_SM2 Current" \
+    "CHRG Voltage" \
+    "CHRG Current" \
+    "DDR2 Voltage" \
+    "DDR2 Current" \
+    "SYS_SM0 Voltage" \
+    "SYS_SM0 Current" \
+    "BL Voltage" \
+    "BL Current" \
+    "PNL Voltage" \
+    "PNL Current" \
+    1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
diff --git a/power_tools.c b/power_tools.c
new file mode 100644
index 0000000..36828be
--- /dev/null
+++ b/power_tools.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of NVIDIA nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* needed for strdup() and asprintf() */
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/hiddev.h>
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "seaboard.h"
+
+static char *prog_name;
+
+struct state {
+    char *hiddev_filename;
+    int fd;
+    int timestamps;
+    int no_names;
+};
+
+#define outerr(fmt, args...) fprintf(stderr, "%s: " fmt "\n", prog_name, ## args)
+
+#define perr(fmt, args...) outerr(fmt ": %s", ## args, strerror(errno))
+
+/* Constants to identify the HW */
+#define NVIDIA_VENDOR_ID 0x955
+#define USB_DAC_PRODUCT_ID 0x9
+
+static int is_usage_current(int usage) {
+    /* Odd usages are current */
+    return !!(usage & 1);
+}
+
+static int probe_from_usage(int usage) {
+    /* Each probe covers two consecutive usages, so this gets the probe index */
+    return usage >> 1;
+}
+
+static double scale_reading(int input, int usage) {
+    if (is_usage_current(usage)) {
+        static const double scale_factor = 3.3 / 1023. / 100.;
+        double output = input * scale_factor;
+        output /= seaboard_probes[probe_from_usage(usage)].resistance;
+        return output;
+    } else {
+        static const double scale_factor = 3.3 / 1023. / 0.1708;
+        double output = input * scale_factor;
+        return output;
+    }
+}
+
+static void print_data(struct state *s, double value, int usage,
+                       uint64_t time) {
+    const char *name = seaboard_probes[probe_from_usage(usage)].name;
+    const char *type = is_usage_current(usage) ? "CURR" : "VOLT";
+
+    if (s->timestamps) {
+        printf("%" PRIu64 ": ", time);
+    }
+
+    if (s->no_names) {
+        printf("%02u:%.16f\n", usage, value);
+    } else {
+        printf("%02u %8s %s %.16f\n", usage, name, type, value);
+    }
+}
+
+static uint64_t gettime64() {
+    struct timeval tv;
+    if (gettimeofday(&tv, NULL)) {
+        perr("gettimeofday failed");
+        return 0;
+    }
+
+    return (uint64_t) tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+#define USB_HID_ORDINAL_USAGE_PAGE 0xa
+
+static int read_data(struct state *s) {
+    struct hiddev_event events[1024];
+    ssize_t num_read;
+
+retry:
+    while ((num_read = read(s->fd, &events, sizeof(events))) > 0) {
+        int i;
+        uint64_t time = gettime64();
+
+        for (i = 0; i < num_read / sizeof(events[0]); i += 1) {
+            double scaled_value;
+            int usage_page = events[i].hid >> 16;
+            int usage_code = (events[i].hid & 0xffff) - 1;
+
+            /* All of the "data" reports are "ordinal", skip the rest */
+            if (usage_page != USB_HID_ORDINAL_USAGE_PAGE)
+                continue;
+
+            scaled_value = scale_reading(events[i].value, usage_code);
+
+            print_data(s, scaled_value, usage_code, time);
+        }
+
+        /* Without this, realtime graph may lag */
+        fflush(NULL);
+    }
+
+    if (num_read < 0) {
+        int err = errno;
+        if (err == EINTR) {
+            goto retry;
+        }
+        perr("Failed to read from USB device");
+        return err;
+    }
+
+    return 0;
+}
+
+static int try_open_device(struct state *s, const char *filename) {
+    int fd = -1;
+    struct hiddev_devinfo dev_info = { 0 };
+    int ret;
+
+    fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        ret = errno;
+        perr("failed to open file '%s'", filename);
+        goto fail;
+    }
+
+    /* Check if we're looking at a NVIDIA USB DAC device */
+    ret = ioctl(fd, HIDIOCGDEVINFO, &dev_info);
+    if (ret) {
+        perr("Failed to read device info for %s", filename);
+        goto fail;
+    }
+
+    if (dev_info.vendor != NVIDIA_VENDOR_ID ||
+        dev_info.product != USB_DAC_PRODUCT_ID) {
+        ret = EINVAL;
+        goto fail;
+    }
+
+    s->fd = fd;
+    return 0;
+
+fail:
+    if (fd >= 0) {
+        close(fd);
+    }
+
+    return ret;
+}
+
+static int find_hiddev(struct state *s) {
+    static char *dev_usb_dir = "/dev/usb";
+    struct dirent *dirent;
+    DIR *dir;
+
+    /* If the user picked a filename, just use that */
+    if (s->hiddev_filename)
+        return try_open_device(s, s->hiddev_filename);
+
+    /* Look for files /dev/usb/hiddev* */
+    dir = opendir(dev_usb_dir);
+    if (!dir) {
+        int err = errno;
+        perr("Failed to open %s", dev_usb_dir);
+        return err;
+    }
+
+    while ((dirent = readdir(dir))) {
+        char *filename = NULL;
+
+#if defined(_DIRENT_HAVE_D_TYPE)
+        if (dirent->d_type != DT_CHR)
+            continue;
+#endif
+
+        if (asprintf(&filename, "%s/%s", dev_usb_dir, dirent->d_name) == -1)
+            return ENOMEM;
+
+        if (!try_open_device(s, filename)) {
+            fprintf(stderr, "Found USB device at %s\n", filename);
+            s->hiddev_filename = filename;
+            return 0;
+        }
+
+        free(filename);
+        filename = NULL;
+    }
+
+    return ENOENT;
+}
+
+static void print_help(void) {
+    fprintf(stderr, "Usage: %s <options>\n\n", prog_name);
+    fprintf(stderr, "Valid options:\n");
+    fprintf(stderr, "--help, -h\n");
+    fprintf(stderr, "    Print this help text and exit\n");
+    fprintf(stderr, "--hiddev_file <path>, -f <path>\n");
+    fprintf(stderr, "    Manually specify the hiddev device file to open\n");
+    fprintf(stderr, "    Default: autodetect\n");
+    fprintf(stderr, "--timestamps, -t\n");
+    fprintf(stderr, "    Output timestamps. Default: off\n");
+    fprintf(stderr, "--short, -s\n");
+    fprintf(stderr, "    Don't print strings describing the readings (useful "
+                    "for scripting)\n");
+    fprintf(stderr, "    Default: print the strings\n");
+}
+
+static int parse_options(struct state *s, int argc, char **argv) {
+    const struct option longopts[] = {
+        { .name = "help",
+          .val = 'h', },
+        { .name = "hiddev_file",
+          .has_arg = 1,
+          .val = 'f', },
+        { .name = "timestamps",
+          .val = 't', },
+        { .name = "short",
+          .val = 's', },
+    };
+    const char *shortopts = "f:hts";
+    char opt;
+
+    while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
+        switch (opt) {
+        case 'f':
+            s->hiddev_filename = strdup(optarg);
+            if (!s->hiddev_filename) {
+                return ENOMEM;
+            }
+            break;
+        case 't':
+            s->timestamps = 1;
+            break;
+        case 's':
+            s->no_names = 1;
+            break;
+        case 'h':
+            print_help();
+            exit(0);
+        case '?':
+            return EINVAL;
+        }
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    static struct state s;
+    prog_name = argv[0];
+
+    if (parse_options(&s, argc, argv)) {
+        print_help();
+        return 1;
+    }
+
+    if (find_hiddev(&s)) {
+        fprintf(stderr, "%s: Failed to find USB device, aborting\n", prog_name);
+        return 1;
+    }
+
+    if (read_data(&s)) {
+        fprintf(stderr, "%s: Failed to read data, aborting\n", prog_name);
+        return 1;
+    }
+
+    close(s.fd);
+
+    return 0;
+}
diff --git a/power_tools.h b/power_tools.h
new file mode 100644
index 0000000..0dc7803
--- /dev/null
+++ b/power_tools.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of NVIDIA nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __POWER_TOOLS_H__
+#define __POWER_TOOLS_H__
+
+struct probe_data {
+    const char *name;
+    /* The value of the resistor for differential readings */
+    double resistance;
+};
+
+#endif /* __POWER_TOOLS_H__ */
diff --git a/seaboard.h b/seaboard.h
new file mode 100644
index 0000000..19334ba
--- /dev/null
+++ b/seaboard.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010, NVIDIA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ *  * Neither the name of NVIDIA nor the names of its contributors may
+ *    be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SEABOARD_H__
+#define __SEABOARD_H__
+
+#include "power_tools.h"
+
+/* This is derived from the board schematics */
+static const struct probe_data seaboard_probes[] = {
+    { .name = "BAT",     .resistance = 0.01, },
+    { .name = "SYS_SM1", .resistance = 0.02, },
+    { .name = "SYS_SM2", .resistance = 0.02, },
+    { .name = "CHRG",    .resistance = 0.01, },
+    { .name = "DDR2",    .resistance = 0.05, },
+    { .name = "SYS_SM0", .resistance = 0.05, },
+    { .name = "BL",      .resistance = 0.02, },
+    { .name = "PNL",     .resistance = 0.01, },
+};
+
+#endif /* __SEABOARD_H__ */