1 /*
2 * Copyright © 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <ctype.h>
25 #include <errno.h>
26 #include <regex.h>
27 #include <stdbool.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include "intel_reg_spec.h"
34
35 static const struct port_desc port_descs[] = {
36 {
37 .name = "mmio",
38 .port = PORT_MMIO,
39 .stride = 4,
40 },
41 {
42 .name = "portio-vga",
43 .port = PORT_PORTIO_VGA,
44 .stride = 1,
45 },
46 {
47 .name = "mmio-vga",
48 .port = PORT_MMIO_VGA,
49 .stride = 1,
50 },
51 {
52 .name = "bunit",
53 .port = PORT_BUNIT,
54 .stride = 1,
55 },
56 {
57 .name = "punit",
58 .port = PORT_PUNIT,
59 .stride = 1,
60 },
61 {
62 .name = "nc",
63 .port = PORT_NC,
64 .stride = 4,
65 },
66 {
67 .name = "dpio",
68 .port = PORT_DPIO,
69 .stride = 4,
70 },
71 {
72 .name = "gpio-nc",
73 .port = PORT_GPIO_NC,
74 .stride = 4,
75 },
76 {
77 .name = "gpio_nc",
78 .port = PORT_GPIO_NC,
79 .stride = 4,
80 },
81 {
82 .name = "cck",
83 .port = PORT_CCK,
84 .stride = 1,
85 },
86 {
87 .name = "ccu",
88 .port = PORT_CCU,
89 .stride = 4,
90 },
91 {
92 .name = "dpio2",
93 .port = PORT_DPIO2,
94 .stride = 4,
95 },
96 {
97 .name = "flisdsi",
98 .port = PORT_FLISDSI,
99 .stride = 1,
100 },
101 };
102
103 /*
104 * Parse port desc of the form (PORTNAME|PORTNUM|MMIO-OFFSET) into reg. NULL or
105 * zero length s is regarded as MMIO.
106 */
parse_port_desc(struct reg * reg,const char * s)107 int parse_port_desc(struct reg *reg, const char *s)
108 {
109 enum port_addr port = PORT_NONE;
110 int i;
111
112 if (s && *s) {
113 /* See if port is specified by number. */
114 char *endp;
115 unsigned long n = strtoul(s, &endp, 16);
116 if (endp > s && *endp == 0) {
117 if (n > PORT_MAX) {
118 /* Not a sideband port, assume MMIO offset. */
119 port = PORT_MMIO;
120 reg->mmio_offset = n;
121 } else {
122 port = n;
123 reg->mmio_offset = 0;
124 }
125 } else {
126 reg->mmio_offset = 0;
127 }
128 } else {
129 /* No port, default to searching for MMIO. */
130 port = PORT_MMIO;
131 reg->mmio_offset = 0;
132 }
133
134 for (i = 0; i < ARRAY_SIZE(port_descs); i++) {
135 if ((port != PORT_NONE && port_descs[i].port == port) ||
136 (s && strcasecmp(s, port_descs[i].name) == 0)) {
137 reg->port_desc = port_descs[i];
138 return 0;
139 }
140 }
141
142 return -1;
143 }
144
skip_space(const char * line)145 static const char *skip_space(const char *line)
146 {
147 while (*line && isspace(*line))
148 line++;
149
150 return line;
151 }
152
ignore_line(const char * line)153 static bool ignore_line(const char *line)
154 {
155 line = skip_space(line);
156
157 switch (*line) {
158 case '\0':
159 case '#':
160 case ';':
161 return true;
162 case '/':
163 return *(line + 1) == '/';
164 }
165
166 return false;
167 }
168
include_file(const char * line,const char * source)169 static char *include_file(const char *line, const char *source)
170 {
171 char *filename, *p;
172
173 line = skip_space(line);
174 if (*line == '(')
175 return NULL;
176
177 /* this'll be plenty */
178 filename = malloc(strlen(source) + strlen(line) + 1);
179 if (!filename)
180 return NULL;
181
182 p = strrchr(source, '/');
183 if (p && *line != '/') {
184 int len = p - source + 1;
185
186 memcpy(filename, source, len);
187 strcpy(filename + len, line);
188 } else {
189 strcpy(filename, line);
190 }
191
192 p = strchr(filename, '\n');
193 if (p)
194 *p = '\0';
195
196 return filename;
197 }
198
199 #define SPC "[[:space:]]*"
200 #define SEP SPC "," SPC
201 #define BEG "^" SPC "\\(" SPC
202 #define END SPC "\\)" SPC "$"
203 #define VALUE "([[:print:]]*)"
204 #define QVALUE "'" VALUE "'"
205 #define REGEXP BEG QVALUE SEP QVALUE SEP QVALUE END
206
parse_line(struct reg * reg,const char * line)207 static int parse_line(struct reg *reg, const char *line)
208 {
209 static regex_t regex;
210 static bool initialized = false;
211 regmatch_t match[4];
212 int i, ret;
213
214 if (!initialized) {
215 if (regcomp (®ex, REGEXP, REG_EXTENDED)) {
216 fprintf(stderr, "regcomp %s\n", REGEXP);
217 return -1;
218 }
219 initialized = true;
220 }
221
222 ret = regexec(®ex, line, ARRAY_SIZE(match), match, 0);
223 if (ret)
224 ret = -1;
225
226 for (i = 1; i < ARRAY_SIZE(match) && ret == 0; i++) {
227 char *p, *e;
228
229 p = strndup(line + match[i].rm_so,
230 match[i].rm_eo - match[i].rm_so);
231
232 if (i == 1) {
233 reg->name = p;
234 } else if (i == 2) {
235 reg->addr = strtoul(p, &e, 16);
236 free(p);
237 if (*e)
238 ret = -1;
239 } else if (i == 3) {
240 ret = parse_port_desc(reg, p);
241 free(p);
242 }
243 }
244
245 if (ret)
246 free(reg->name);
247
248 return ret;
249 }
250
parse_file(struct reg ** regs,size_t * nregs,ssize_t index,const char * filename)251 static ssize_t parse_file(struct reg **regs, size_t *nregs,
252 ssize_t index, const char *filename)
253 {
254 FILE *file;
255 char *line = NULL, *include;
256 size_t linesize = 0;
257 int lineno = 0, r;
258 ssize_t ret = -1;
259
260 file = fopen(filename, "r");
261 if (!file) {
262 fprintf(stderr, "Error: fopen '%s': %s\n",
263 filename, strerror(errno));
264 return -1;
265 }
266
267 while (getline(&line, &linesize, file) != -1) {
268 struct reg reg = {};
269
270 lineno++;
271
272 if (ignore_line(line))
273 continue;
274
275 include = include_file(line, filename);
276 if (include) {
277 index = parse_file(regs, nregs, index, include);
278 free(include);
279 if (index < 0) {
280 fprintf(stderr, "Error: %s:%d: %s",
281 filename, lineno, line);
282 goto out;
283 }
284 continue;
285 }
286
287 r = parse_line(®, line);
288 if (r < 0) {
289 fprintf(stderr, "Error: %s:%d: %s",
290 filename, lineno, line);
291 goto out;
292 } else if (r) {
293 continue;
294 }
295
296 if (!*regs || index >= *nregs) {
297 if (!*regs)
298 *nregs = 64;
299 else
300 *nregs *= 2;
301
302 *regs = recalloc(*regs, *nregs, sizeof(**regs));
303 if (!*regs) {
304 fprintf(stderr, "Error: %s\n", strerror(ENOMEM));
305 goto out;
306 }
307 }
308
309 (*regs)[index++] = reg;
310 }
311
312 ret = index;
313
314 out:
315 free(line);
316 fclose(file);
317
318 return ret;
319 }
320
321 /*
322 * Get register definitions from file.
323 */
intel_reg_spec_file(struct reg ** regs,const char * file)324 ssize_t intel_reg_spec_file(struct reg **regs, const char *file)
325 {
326 size_t nregs = 0;
327 *regs = NULL;
328
329 return parse_file(regs, &nregs, 0, file);
330 }
331
332 /*
333 * Free the memory allocated for register definitions.
334 */
intel_reg_spec_free(struct reg * regs,size_t n)335 void intel_reg_spec_free(struct reg *regs, size_t n)
336 {
337 size_t i;
338
339 for (i = 0; i < n; i++) {
340 free(regs[i].name);
341 }
342 free(regs);
343 }
344
intel_reg_spec_print_ports(void)345 void intel_reg_spec_print_ports(void)
346 {
347 int i;
348
349 for (i = 0; i < ARRAY_SIZE(port_descs); i++)
350 printf("%s%s", i == 0 ? "" : ", ", port_descs[i].name);
351 }
352