| /* |
| * 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 = 1, |
| }, |
| { |
| .name = "mmio-vga", |
| .port = PORT_MMIO_VGA, |
| .stride = 1, |
| }, |
| { |
| .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; |
| } |
| |
| 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); |
| } |
| |
| void intel_reg_spec_print_ports(void) |
| { |
| int i; |
| |
| for (i = 0; i < ARRAY_SIZE(port_descs); i++) |
| printf("%s%s", i == 0 ? "" : ", ", port_descs[i].name); |
| } |