You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

352 lines
6.6 KiB

/*
* 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 (&regex, REGEXP, REG_EXTENDED)) {
fprintf(stderr, "regcomp %s\n", REGEXP);
return -1;
}
initialized = true;
}
ret = regexec(&regex, 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(&reg, 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);
}