summaryrefslogtreecommitdiff
path: root/tools/intel_reg.c
diff options
context:
space:
mode:
authorJani Nikula <jani.nikula@intel.com>2014-12-10 14:27:07 +0200
committerJani Nikula <jani.nikula@intel.com>2015-04-23 01:09:59 +0300
commitdfda0b6aeccef464cc6f1af60d8ea16c11fb13f7 (patch)
tree817f77e03af6bc511faceb43ab1a3ca24ff57507 /tools/intel_reg.c
parente1ce5eac338f44deda488a44db04faceea12f13a (diff)
intel_reg: introduce one intel_reg tool to rule them all
Three Tools for the Elven-kings under the sky, Seven for the Dwarf-lords in their halls of stone, Nine for Mortal Men doomed to die, One for the Dark Lord on his dark throne In the Land of Mordor where the Shadows lie. One Tool to rule them all, One Tool to find them, One Tool to bring them all and in the darkness bind them In the Land of Mordor where the Shadows lie. J.R.R. Tolkien's epigraph to The Lord of The Tools | sed 's/Ring/Tool/g' Introduce intel_reg as the one Intel graphics register multitool to replace intel_reg_read, intel_reg_write, intel_iosf_sb_read, intel_iosf_sb_write, intel_vga_read, intel_vga_write, intel_reg_dumper, intel_reg_snapshot, and quick_dump.py. Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'tools/intel_reg.c')
-rw-r--r--tools/intel_reg.c899
1 files changed, 899 insertions, 0 deletions
diff --git a/tools/intel_reg.c b/tools/intel_reg.c
new file mode 100644
index 00000000..975529d4
--- /dev/null
+++ b/tools/intel_reg.c
@@ -0,0 +1,899 @@
+/*
+ * Copyright © 2015 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.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/io.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "intel_io.h"
+#include "intel_chipset.h"
+
+#include "intel_reg_spec.h"
+
+struct config {
+ struct pci_device *pci_dev;
+ char *mmiofile;
+ uint32_t devid;
+
+ /* read: number of registers to read */
+ uint32_t count;
+
+ /* write: do a posting read */
+ bool post;
+
+ /* decode register for all platforms */
+ bool all_platforms;
+
+ /* spread out bits for convenience */
+ bool binary;
+
+ /* register spec */
+ char *specfile;
+ struct reg *regs;
+ ssize_t regcount;
+
+ int verbosity;
+};
+
+/* port desc must have been set */
+static int set_reg_by_addr(struct config *config, struct reg *reg,
+ uint32_t addr)
+{
+ int i;
+
+ reg->addr = addr;
+ if (reg->name)
+ free(reg->name);
+ reg->name = NULL;
+
+ for (i = 0; i < config->regcount; i++) {
+ struct reg *r = &config->regs[i];
+
+ if (reg->port_desc.port != r->port_desc.port)
+ continue;
+
+ /* ->mmio_offset should be 0 for non-MMIO ports. */
+ if (addr + reg->mmio_offset == r->addr + r->mmio_offset) {
+ /* Always output the "normalized" offset+addr. */
+ reg->mmio_offset = r->mmio_offset;
+ reg->addr = r->addr;
+
+ reg->name = r->name ? strdup(r->name) : NULL;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/* port desc must have been set */
+static int set_reg_by_name(struct config *config, struct reg *reg,
+ const char *name)
+{
+ int i;
+
+ reg->name = strdup(name);
+ reg->addr = 0;
+
+ for (i = 0; i < config->regcount; i++) {
+ struct reg *r = &config->regs[i];
+
+ if (reg->port_desc.port != r->port_desc.port)
+ continue;
+
+ if (!r->name)
+ continue;
+
+ if (strcasecmp(name, r->name) == 0) {
+ reg->addr = r->addr;
+
+ /* Also get MMIO offset if not already specified. */
+ if (!reg->mmio_offset && r->mmio_offset)
+ reg->mmio_offset = r->mmio_offset;
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static void to_binary(char *buf, size_t buflen, uint32_t val)
+{
+ int i;
+
+ if (!buflen)
+ return;
+
+ *buf = '\0';
+
+ /* XXX: This quick and dirty implementation makes eyes hurt. */
+ for (i = 31; i >= 0; i--) {
+ if (i % 8 == 0)
+ snprintf(buf, buflen, " %2d", i);
+ else
+ snprintf(buf, buflen, " ");
+ buflen -= strlen(buf);
+ buf += strlen(buf);
+ }
+ snprintf(buf, buflen, "\n");
+ buflen -= strlen(buf);
+ buf += strlen(buf);
+
+ for (i = 31; i >= 0; i--) {
+ snprintf(buf, buflen, " %s%d", i % 8 == 7 ? " " : "",
+ !!(val & (1 << i)));
+ buflen -= strlen(buf);
+ buf += strlen(buf);
+ }
+ snprintf(buf, buflen, "\n");
+}
+
+static void dump_decode(struct config *config, struct reg *reg, uint32_t val)
+{
+ char decode[1024];
+ char tmp[1024];
+ char bin[1024];
+
+ if (config->binary)
+ to_binary(bin, sizeof(bin), val);
+ else
+ *bin = '\0';
+
+ intel_reg_spec_decode(tmp, sizeof(tmp), reg, val,
+ config->all_platforms ? 0 : config->devid);
+
+ if (*tmp) {
+ /* We have a decode result, and maybe binary decode. */
+ if (config->all_platforms)
+ snprintf(decode, sizeof(decode), "\n%s%s", tmp, bin);
+ else
+ snprintf(decode, sizeof(decode), " (%s)\n%s", tmp, bin);
+ } else if (*bin) {
+ /* No decode result, but binary decode. */
+ snprintf(decode, sizeof(decode), "\n%s", bin);
+ } else {
+ /* No decode nor binary decode. */
+ snprintf(decode, sizeof(decode), "\n");
+ }
+
+ if (reg->port_desc.port == PORT_MMIO) {
+ /* Omit port name for MMIO, optionally include MMIO offset. */
+ if (reg->mmio_offset)
+ printf("%24s (0x%08x:0x%08x): 0x%08x%s",
+ reg->name ?: "",
+ reg->mmio_offset, reg->addr,
+ val, decode);
+ else
+ printf("%35s (0x%08x): 0x%08x%s",
+ reg->name ?: "",
+ reg->addr,
+ val, decode);
+ } else {
+ char name[100], addr[100];
+
+ /* If no name, use addr as name for easier copy pasting. */
+ if (reg->name)
+ snprintf(name, sizeof(name), "%s:%s",
+ reg->port_desc.name, reg->name);
+ else
+ snprintf(name, sizeof(name), "%s:0x%08x",
+ reg->port_desc.name, reg->addr);
+
+ /* Negative port numbers are not real sideband ports. */
+ if (reg->port_desc.port > PORT_NONE)
+ snprintf(addr, sizeof(addr), "0x%02x:0x%08x",
+ reg->port_desc.port, reg->addr);
+ else
+ snprintf(addr, sizeof(addr), "%s:0x%08x",
+ reg->port_desc.name, reg->addr);
+
+ printf("%24s (%s): 0x%08x%s", name, addr, val, decode);
+ }
+}
+
+static int read_register(struct config *config, struct reg *reg, uint32_t *valp)
+{
+ uint32_t val = 0;
+
+ switch (reg->port_desc.port) {
+ case PORT_MMIO:
+ val = *(volatile uint32_t *)((volatile char*)mmio +
+ reg->mmio_offset + reg->addr);
+ break;
+ case PORT_PORTIO_VGA:
+ iopl(3);
+ val = inb(reg->addr);
+ iopl(0);
+ break;
+ case PORT_MMIO_VGA:
+ val = *((volatile uint8_t*)mmio + reg->addr);
+ break;
+ case PORT_BUNIT:
+ case PORT_PUNIT:
+ case PORT_NC:
+ case PORT_DPIO:
+ case PORT_GPIO_NC:
+ case PORT_CCK:
+ case PORT_CCU:
+ case PORT_DPIO2:
+ case PORT_FLISDSI:
+ if (!IS_VALLEYVIEW(config->devid) &&
+ !IS_CHERRYVIEW(config->devid)) {
+ fprintf(stderr, "port %s only supported on vlv/chv\n",
+ reg->port_desc.name);
+ return -1;
+ }
+ val = intel_iosf_sb_read(reg->port_desc.port, reg->addr);
+ break;
+ default:
+ fprintf(stderr, "port %d not supported\n", reg->port_desc.port);
+ return -1;
+ }
+
+ if (valp)
+ *valp = val;
+
+ return 0;
+}
+
+static void dump_register(struct config *config, struct reg *reg)
+{
+ uint32_t val;
+
+ if (read_register(config, reg, &val) == 0)
+ dump_decode(config, reg, val);
+}
+
+static int write_register(struct config *config, struct reg *reg, uint32_t val)
+{
+ int ret = 0;
+
+ if (config->verbosity > 0) {
+ printf("Before:\n");
+ dump_register(config, reg);
+ }
+
+ switch (reg->port_desc.port) {
+ case PORT_MMIO:
+ *(volatile uint32_t *)((volatile char *)mmio +
+ reg->mmio_offset + reg->addr) = val;
+ break;
+ case PORT_PORTIO_VGA:
+ if (val > 0xff) {
+ fprintf(stderr, "value 0x%08x out of range for port %s\n",
+ val, reg->port_desc.name);
+ return -1;
+ }
+ iopl(3);
+ outb(val, reg->addr);
+ iopl(0);
+ break;
+ case PORT_MMIO_VGA:
+ if (val > 0xff) {
+ fprintf(stderr, "value 0x%08x out of range for port %s\n",
+ val, reg->port_desc.name);
+ return -1;
+ }
+ *((volatile uint8_t *)mmio + reg->addr) = val;
+ break;
+ case PORT_BUNIT:
+ case PORT_PUNIT:
+ case PORT_NC:
+ case PORT_DPIO:
+ case PORT_GPIO_NC:
+ case PORT_CCK:
+ case PORT_CCU:
+ case PORT_DPIO2:
+ case PORT_FLISDSI:
+ if (!IS_VALLEYVIEW(config->devid) &&
+ !IS_CHERRYVIEW(config->devid)) {
+ fprintf(stderr, "port %s only supported on vlv/chv\n",
+ reg->port_desc.name);
+ return -1;
+ }
+ intel_iosf_sb_write(reg->port_desc.port, reg->addr, val);
+ break;
+ default:
+ fprintf(stderr, "port %d not supported\n", reg->port_desc.port);
+ ret = -1;
+ }
+
+ if (config->verbosity > 0) {
+ printf("After:\n");
+ dump_register(config, reg);
+ } else if (config->post) {
+ read_register(config, reg, NULL);
+ }
+
+ return ret;
+}
+
+/* s has [(PORTNAME|PORTNUM|MMIO-OFFSET):](REGNAME|REGADDR) */
+static int parse_reg(struct config *config, struct reg *reg, const char *s)
+{
+ unsigned long addr;
+ char *endp;
+ const char *p;
+ int ret;
+
+ memset(reg, 0, sizeof(*reg));
+
+ p = strchr(s, ':');
+ if (p == s) {
+ ret = -1;
+ } else if (p) {
+ char *port_name = strndup(s, p - s);
+
+ ret = parse_port_desc(reg, port_name);
+
+ free(port_name);
+ p++;
+ } else {
+ /*
+ * XXX: If port is not specified in input, see if the register
+ * matches by name, and initialize port desc based on that.
+ */
+ ret = parse_port_desc(reg, NULL);
+ p = s;
+ }
+
+ if (ret) {
+ fprintf(stderr, "invalid port in '%s'\n", s);
+ return ret;
+ }
+
+ addr = strtoul(p, &endp, 16);
+ if (endp > p && *endp == 0) {
+ /* It's a number. */
+ ret = set_reg_by_addr(config, reg, addr);
+ } else {
+ /* Not a number, it's a name. */
+ ret = set_reg_by_name(config, reg, p);
+ }
+
+ return ret;
+}
+
+/* XXX: add support for register ranges, maybe REGISTER..REGISTER */
+static int intel_reg_read(struct config *config, int argc, char *argv[])
+{
+ int i, j;
+
+ if (argc == 1) {
+ fprintf(stderr, "read: no registers specified\n");
+ return EXIT_FAILURE;
+ }
+
+ if (config->mmiofile)
+ intel_mmio_use_dump_file(config->mmiofile);
+ else
+ intel_register_access_init(config->pci_dev, 0);
+
+ for (i = 1; i < argc; i++) {
+ struct reg reg;
+
+ if (parse_reg(config, &reg, argv[i]))
+ continue;
+
+ for (j = 0; j < config->count; j++) {
+ dump_register(config, &reg);
+ /* Update addr and name. */
+ set_reg_by_addr(config, &reg,
+ reg.addr + reg.port_desc.stride);
+ }
+ }
+
+ intel_register_access_fini();
+
+ return EXIT_SUCCESS;
+}
+
+static int intel_reg_write(struct config *config, int argc, char *argv[])
+{
+ int i;
+
+ if (argc == 1) {
+ fprintf(stderr, "write: no registers specified\n");
+ return EXIT_FAILURE;
+ }
+
+ intel_register_access_init(intel_get_pci_device(), 0);
+
+ for (i = 1; i < argc; i += 2) {
+ struct reg reg;
+ uint32_t val;
+ char *endp;
+
+ if (parse_reg(config, &reg, argv[i]))
+ continue;
+
+ if (i + 1 == argc) {
+ fprintf(stderr, "write: no value\n");
+ break;
+ }
+
+ val = strtoul(argv[i + 1], &endp, 16);
+ if (endp == argv[i + 1] || *endp) {
+ fprintf(stderr, "write: invalid value '%s'\n",
+ argv[i + 1]);
+ continue;
+ }
+
+ write_register(config, &reg, val);
+ }
+
+ intel_register_access_fini();
+
+ return EXIT_SUCCESS;
+}
+
+static int intel_reg_dump(struct config *config, int argc, char *argv[])
+{
+ struct reg *reg;
+ int i;
+
+ if (config->mmiofile)
+ intel_mmio_use_dump_file(config->mmiofile);
+ else
+ intel_register_access_init(config->pci_dev, 0);
+
+ for (i = 0; i < config->regcount; i++) {
+ reg = &config->regs[i];
+
+ /* can't dump sideband with mmiofile */
+ if (config->mmiofile && reg->port_desc.port != PORT_MMIO)
+ continue;
+
+ dump_register(config, &config->regs[i]);
+ }
+
+ intel_register_access_fini();
+
+ return EXIT_FAILURE;
+}
+
+static int intel_reg_snapshot(struct config *config, int argc, char *argv[])
+{
+ int mmio_bar = IS_GEN2(config->devid) ? 1 : 0;
+
+ if (config->mmiofile) {
+ fprintf(stderr, "specifying --mmio=FILE is not compatible\n");
+ return EXIT_FAILURE;
+ }
+
+ intel_mmio_use_pci_bar(config->pci_dev);
+
+ /* XXX: error handling */
+ write(1, mmio, config->pci_dev->regions[mmio_bar].size);
+
+ if (config->verbosity > 0)
+ printf("use this with --mmio=FILE --devid=0x%04X\n",
+ config->devid);
+
+ return EXIT_SUCCESS;
+}
+
+/* XXX: add support for reading and re-decoding a previously done dump */
+static int intel_reg_decode(struct config *config, int argc, char *argv[])
+{
+ int i;
+
+ if (argc == 1) {
+ fprintf(stderr, "decode: no registers specified\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = 1; i < argc; i += 2) {
+ struct reg reg;
+ uint32_t val;
+ char *endp;
+
+ if (parse_reg(config, &reg, argv[i]))
+ continue;
+
+ if (i + 1 == argc) {
+ fprintf(stderr, "decode: no value\n");
+ break;
+ }
+
+ val = strtoul(argv[i + 1], &endp, 16);
+ if (endp == argv[i + 1] || *endp) {
+ fprintf(stderr, "decode: invalid value '%s'\n",
+ argv[i + 1]);
+ continue;
+ }
+
+ dump_decode(config, &reg, val);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int intel_reg_list(struct config *config, int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < config->regcount; i++) {
+ printf("%s\n", config->regs[i].name);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int intel_reg_help(struct config *config, int argc, char *argv[]);
+
+struct command {
+ const char *name;
+ const char *description;
+ const char *synopsis;
+ int (*function)(struct config *config, int argc, char *argv[]);
+};
+
+static const struct command commands[] = {
+ {
+ .name = "read",
+ .function = intel_reg_read,
+ .synopsis = "[--count=N] REGISTER [...]",
+ .description = "read and decode specified register(s)",
+ },
+ {
+ .name = "write",
+ .function = intel_reg_write,
+ .synopsis = "[--post] REGISTER VALUE [REGISTER VALUE ...]",
+ .description = "write value(s) to specified register(s)",
+ },
+ {
+ .name = "dump",
+ .function = intel_reg_dump,
+ .description = "dump all known registers",
+ },
+ {
+ .name = "decode",
+ .function = intel_reg_decode,
+ .synopsis = "REGISTER VALUE [REGISTER VALUE ...]",
+ .description = "decode value(s) for specified register(s)",
+ },
+ {
+ .name = "snapshot",
+ .function = intel_reg_snapshot,
+ .description = "create a snapshot of the MMIO bar to stdout",
+ },
+ {
+ .name = "list",
+ .function = intel_reg_list,
+ .description = "list all known register names",
+ },
+ {
+ .name = "help",
+ .function = intel_reg_help,
+ .description = "show this help",
+ },
+};
+
+static int intel_reg_help(struct config *config, int argc, char *argv[])
+{
+ int i;
+
+ printf("Intel graphics register multitool\n\n");
+ printf("Usage: intel_reg [OPTION ...] COMMAND\n\n");
+ printf("COMMAND is one of:\n");
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ printf(" %-14s%s\n", commands[i].name,
+ commands[i].synopsis ?: "");
+ printf(" %-14s%s\n", "", commands[i].description);
+ }
+
+ printf("\n");
+ printf("REGISTER is defined as:\n");
+ printf(" [(PORTNAME|PORTNUM|MMIO-OFFSET):](REGNAME|REGADDR)\n");
+
+ printf("\n");
+ printf("OPTIONS common to most COMMANDS:\n");
+ printf(" --spec=PATH Read register spec from directory or file\n");
+ printf(" --mmio=FILE Use an MMIO snapshot\n");
+ printf(" --devid=DEVID Specify PCI device ID for --mmio=FILE\n");
+ printf(" --all Decode registers for all known platforms\n");
+ printf(" --binary Binary dump registers\n");
+ printf(" --verbose Increase verbosity\n");
+ printf(" --quiet Reduce verbosity\n");
+
+ printf("\n");
+ printf("Environment variables:\n");
+ printf(" INTEL_REG_SPEC Read register spec from directory or file\n");
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Get codename for a gen5+ platform to be used for finding register spec file.
+ */
+static const char *get_codename(uint32_t devid)
+{
+ if (IS_GEN5(devid))
+ return "ironlake";
+ else if (IS_GEN6(devid))
+ return "sandybridge";
+ else if (IS_IVYBRIDGE(devid))
+ return "ivybridge";
+ else if (IS_HASWELL(devid))
+ return "haswell";
+ else if (IS_BROADWELL(devid))
+ return "broadwell";
+ else if (IS_SKYLAKE(devid))
+ return "skylake";
+ else if (IS_CHERRYVIEW(devid))
+ return "cherryview";
+ else if (IS_VALLEYVIEW(devid))
+ return "valleyview";
+
+ return NULL;
+}
+
+/*
+ * Get register definitions filename for devid in dir. Return 0 if found,
+ * negative error code otherwise.
+ */
+static int get_reg_spec_file(char *buf, size_t buflen, const char *dir,
+ uint32_t devid)
+{
+ const char *codename;
+
+ /* First, try file named after devid, e.g. "0412" for Haswell GT2. */
+ snprintf(buf, buflen, "%s/%04x", dir, devid);
+ if (!access(buf, F_OK))
+ return 0;
+
+ /*
+ * Second, for gen5+, try file named after codename, e.g. "haswell" for
+ * Haswell.
+ */
+ codename = get_codename(devid);
+ if (codename) {
+ snprintf(buf, buflen, "%s/%s", dir, codename);
+ if (!access(buf, F_OK))
+ return 0;
+ }
+
+ /*
+ * Third, try file named after gen, e.g. "gen7" for Haswell (which is
+ * technically 7.5 but this is how it works).
+ */
+ snprintf(buf, buflen, "%s/gen%d", dir, intel_gen(devid));
+ if (!access(buf, F_OK))
+ return 0;
+
+ return -ENOENT;
+}
+
+/*
+ * Read register spec.
+ */
+static int read_reg_spec(struct config *config)
+{
+ char buf[PATH_MAX];
+ char *path;
+ struct stat st;
+ int r;
+
+ path = config->specfile;
+ if (!path)
+ path = getenv("INTEL_REG_SPEC");
+
+ if (!path)
+ goto builtin;
+
+ r = stat(path, &st);
+ if (r) {
+ fprintf(stderr, "Warning: stat '%s' failed: %s. "
+ "Using builtin register spec.\n",
+ path, strerror(errno));
+ goto builtin;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ r = get_reg_spec_file(buf, sizeof(buf), path, config->devid);
+ if (r) {
+ fprintf(stderr, "Warning: register spec not found in "
+ "'%s'. Using builtin register spec.\n", path);
+ goto builtin;
+ }
+ path = buf;
+ }
+
+ config->regcount = intel_reg_spec_file(&config->regs, path);
+ if (config->regcount <= 0) {
+ fprintf(stderr, "Warning: reading '%s' failed. "
+ "Using builtin register spec.\n", path);
+ goto builtin;
+ }
+
+ return config->regcount;
+
+builtin:
+ /* Fallback to builtin register spec. */
+ config->regcount = intel_reg_spec_builtin(&config->regs, config->devid);
+
+ return config->regcount;
+}
+
+enum opt {
+ OPT_UNKNOWN = '?',
+ OPT_END = -1,
+ OPT_MMIO,
+ OPT_DEVID,
+ OPT_COUNT,
+ OPT_POST,
+ OPT_ALL,
+ OPT_BINARY,
+ OPT_SPEC,
+ OPT_VERBOSE,
+ OPT_QUIET,
+ OPT_HELP,
+};
+
+int main(int argc, char *argv[])
+{
+ int ret, i, index;
+ char *endp;
+ enum opt opt;
+ const struct command *command = NULL;
+ struct config config = {
+ .count = 1,
+ };
+ bool help = false;
+
+ static struct option options[] = {
+ /* global options */
+ { "spec", required_argument, NULL, OPT_SPEC },
+ { "verbose", no_argument, NULL, OPT_VERBOSE },
+ { "quiet", no_argument, NULL, OPT_QUIET },
+ { "help", no_argument, NULL, OPT_HELP },
+ /* options specific to read and dump */
+ { "mmio", required_argument, NULL, OPT_MMIO },
+ { "devid", required_argument, NULL, OPT_DEVID },
+ /* options specific to read */
+ { "count", required_argument, NULL, OPT_COUNT },
+ /* options specific to write */
+ { "post", no_argument, NULL, OPT_POST },
+ /* options specific to read, dump and decode */
+ { "all", no_argument, NULL, OPT_ALL },
+ { "binary", no_argument, NULL, OPT_BINARY },
+ { 0 }
+ };
+
+ for (opt = 0; opt != OPT_END; ) {
+ opt = getopt_long(argc, argv, "", options, &index);
+
+ switch (opt) {
+ case OPT_MMIO:
+ config.mmiofile = strdup(optarg);
+ if (!config.mmiofile) {
+ fprintf(stderr, "strdup: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_DEVID:
+ config.devid = strtoul(optarg, &endp, 16);
+ if (*endp) {
+ fprintf(stderr, "invalid devid '%s'\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_COUNT:
+ config.count = strtol(optarg, &endp, 10);
+ if (*endp) {
+ fprintf(stderr, "invalid count '%s'\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_POST:
+ config.post = true;
+ break;
+ case OPT_SPEC:
+ config.specfile = strdup(optarg);
+ if (!config.specfile) {
+ fprintf(stderr, "strdup: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ break;
+ case OPT_ALL:
+ config.all_platforms = true;
+ break;
+ case OPT_BINARY:
+ config.binary = true;
+ break;
+ case OPT_VERBOSE:
+ config.verbosity++;
+ break;
+ case OPT_QUIET:
+ config.verbosity--;
+ break;
+ case OPT_HELP:
+ help = true;
+ break;
+ case OPT_END:
+ break;
+ case OPT_UNKNOWN:
+ return EXIT_FAILURE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (help)
+ return intel_reg_help(&config, argc, argv);
+
+ if (argc == 0) {
+ fprintf(stderr, "Command missing. Try intel_reg help.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (config.mmiofile) {
+ if (!config.devid) {
+ fprintf(stderr, "--mmio requires --devid\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ /* XXX: devid without --mmio could be useful for decode. */
+ if (config.devid) {
+ fprintf(stderr, "--devid without --mmio\n");
+ return EXIT_FAILURE;
+ }
+ config.pci_dev = intel_get_pci_device();
+ config.devid = config.pci_dev->device_id;
+ }
+
+ if (read_reg_spec(&config) < 0) {
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ if (strcmp(argv[0], commands[i].name) == 0) {
+ command = &commands[i];
+ break;
+ }
+ }
+
+ if (!command) {
+ fprintf(stderr, "'%s' is not an intel-reg command\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ ret = command->function(&config, argc, argv);
+
+ free(config.mmiofile);
+
+ return ret;
+}