/* * 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 #include #include #include #include #include #include #include #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); }