diff options
Diffstat (limited to 'tools/intel_reg_spec.c')
-rw-r--r-- | tools/intel_reg_spec.c | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/tools/intel_reg_spec.c b/tools/intel_reg_spec.c new file mode 100644 index 00000000..6b7e30c1 --- /dev/null +++ b/tools/intel_reg_spec.c @@ -0,0 +1,345 @@ +/* + * 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 <ctype.h> +#include <errno.h> +#include <regex.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "intel_reg_spec.h" + +static const struct port_desc port_descs[] = { + { + .name = "mmio", + .port = PORT_MMIO, + .stride = 4, + }, + { + .name = "portio-vga", + .port = PORT_PORTIO_VGA, + .stride = 4, + }, + { + .name = "mmio-vga", + .port = PORT_MMIO_VGA, + .stride = 4, + }, + { + .name = "bunit", + .port = PORT_BUNIT, + .stride = 1, + }, + { + .name = "punit", + .port = PORT_PUNIT, + .stride = 1, + }, + { + .name = "nc", + .port = PORT_NC, + .stride = 4, + }, + { + .name = "dpio", + .port = PORT_DPIO, + .stride = 4, + }, + { + .name = "gpio-nc", + .port = PORT_GPIO_NC, + .stride = 4, + }, + { + .name = "gpio_nc", + .port = PORT_GPIO_NC, + .stride = 4, + }, + { + .name = "cck", + .port = PORT_CCK, + .stride = 1, + }, + { + .name = "ccu", + .port = PORT_CCU, + .stride = 4, + }, + { + .name = "dpio2", + .port = PORT_DPIO2, + .stride = 4, + }, + { + .name = "flisdsi", + .port = PORT_FLISDSI, + .stride = 1, + }, +}; + +/* + * Parse port desc of the form (PORTNAME|PORTNUM|MMIO-OFFSET) into reg. NULL or + * zero length s is regarded as MMIO. + */ +int parse_port_desc(struct reg *reg, const char *s) +{ + enum port_addr port = PORT_NONE; + int i; + + if (s && *s) { + /* See if port is specified by number. */ + char *endp; + unsigned long n = strtoul(s, &endp, 16); + if (endp > s && *endp == 0) { + if (n > PORT_MAX) { + /* Not a sideband port, assume MMIO offset. */ + port = PORT_MMIO; + reg->mmio_offset = n; + } else { + port = n; + reg->mmio_offset = 0; + } + } else { + reg->mmio_offset = 0; + } + } else { + /* No port, default to searching for MMIO. */ + port = PORT_MMIO; + reg->mmio_offset = 0; + } + + for (i = 0; i < ARRAY_SIZE(port_descs); i++) { + if ((port != PORT_NONE && port_descs[i].port == port) || + (s && strcasecmp(s, port_descs[i].name) == 0)) { + reg->port_desc = port_descs[i]; + return 0; + } + } + + return -1; +} + +static const char *skip_space(const char *line) +{ + while (*line && isspace(*line)) + line++; + + return line; +} + +static bool ignore_line(const char *line) +{ + line = skip_space(line); + + switch (*line) { + case '\0': + case '#': + case ';': + return true; + case '/': + return *(line + 1) == '/'; + } + + return false; +} + +static char *include_file(const char *line, const char *source) +{ + char *filename, *p; + + line = skip_space(line); + if (*line == '(') + return NULL; + + /* this'll be plenty */ + filename = malloc(strlen(source) + strlen(line) + 1); + if (!filename) + return NULL; + + p = strrchr(source, '/'); + if (p && *line != '/') { + int len = p - source + 1; + + memcpy(filename, source, len); + strcpy(filename + len, line); + } else { + strcpy(filename, line); + } + + p = strchr(filename, '\n'); + if (p) + *p = '\0'; + + return filename; +} + +#define SPC "[[:space:]]*" +#define SEP SPC "," SPC +#define BEG "^" SPC "\\(" SPC +#define END SPC "\\)" SPC "$" +#define VALUE "([[:print:]]*)" +#define QVALUE "'" VALUE "'" +#define REGEXP BEG QVALUE SEP QVALUE SEP QVALUE END + +static int parse_line(struct reg *reg, const char *line) +{ + static regex_t regex; + static bool initialized = false; + regmatch_t match[4]; + int i, ret; + + if (!initialized) { + if (regcomp (®ex, REGEXP, REG_EXTENDED)) { + fprintf(stderr, "regcomp %s\n", REGEXP); + return -1; + } + initialized = true; + } + + memset(reg, 0, sizeof(*reg)); + + ret = regexec(®ex, line, ARRAY_SIZE(match), match, 0); + if (ret) + ret = -1; + + for (i = 1; i < ARRAY_SIZE(match) && ret == 0; i++) { + char *p, *e; + + p = strndup(line + match[i].rm_so, + match[i].rm_eo - match[i].rm_so); + + if (i == 1) { + reg->name = p; + } else if (i == 2) { + reg->addr = strtoul(p, &e, 16); + free(p); + if (*e) + ret = -1; + } else if (i == 3) { + ret = parse_port_desc(reg, p); + free(p); + } + } + + if (ret) + free(reg->name); + + return ret; +} + +static ssize_t parse_file(struct reg **regs, size_t *nregs, + ssize_t index, const char *filename) +{ + FILE *file; + char *line = NULL, *include; + size_t linesize = 0; + int lineno = 0, r; + ssize_t ret = -1; + + file = fopen(filename, "r"); + if (!file) { + fprintf(stderr, "Error: fopen '%s': %s\n", + filename, strerror(errno)); + return -1; + } + + while (getline(&line, &linesize, file) != -1) { + struct reg reg; + + lineno++; + + if (ignore_line(line)) + continue; + + include = include_file(line, filename); + if (include) { + index = parse_file(regs, nregs, index, include); + free(include); + if (index < 0) { + fprintf(stderr, "Error: %s:%d: %s", + filename, lineno, line); + goto out; + } + continue; + } + + r = parse_line(®, line); + if (r < 0) { + fprintf(stderr, "Error: %s:%d: %s", + filename, lineno, line); + goto out; + } else if (r) { + continue; + } + + if (!*regs || index >= *nregs) { + if (!*regs) + *nregs = 64; + else + *nregs *= 2; + + *regs = recalloc(*regs, *nregs, sizeof(**regs)); + if (!*regs) { + fprintf(stderr, "Error: %s\n", strerror(ENOMEM)); + goto out; + } + } + + (*regs)[index++] = reg; + } + + ret = index; + +out: + free(line); + fclose(file); + + return ret; +} + +/* + * Get register definitions from file. + */ +ssize_t intel_reg_spec_file(struct reg **regs, const char *file) +{ + size_t nregs = 0; + *regs = NULL; + + return parse_file(regs, &nregs, 0, file); +} + +/* + * Free the memory allocated for register definitions. + */ +void intel_reg_spec_free(struct reg *regs, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + free(regs[i].name); + } + free(regs); +} |