subr_hints.c revision 79696
1/*-
2 * Copyright (c) 2000,2001 Peter Wemm <peter@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/kern/subr_hints.c 79696 2001-07-14 00:23:10Z peter $
27 */
28
29#include <sys/param.h>
30#include <sys/systm.h>
31#include <sys/bus.h>
32
33/*
34 * Access functions for device resources.
35 */
36
37extern char static_hints[];		/* by config for now */
38extern int hintmode;			/* 0 = off. 1 = config, 2 = fallback */
39static char *hintp;
40
41/*
42 * Evil wildcarding resource string lookup.
43 * This walks the supplied env string table and returns a match.
44 * The start point can be remembered for incremental searches.
45 */
46static int
47res_find(int *line, int *startln,
48    const char *name, int *unit, const char *resname, const char *value,
49    const char **ret_name, int *ret_namelen, int *ret_unit,
50    const char **ret_resname, int *ret_resnamelen, const char **ret_value)
51{
52	int n = 0, hit;
53	char r_name[32];
54	int r_unit;
55	char r_resname[32];
56	char r_value[128];
57	const char *s, *cp;
58	char *p;
59
60	if (hintp == NULL) {
61		switch (hintmode) {
62		case 0:		/* config supplied nothing */
63			hintp = kern_envp;
64			break;
65		case 1:		/* static hints only */
66			hintp = static_hints;
67			break;
68		case 2:		/* fallback mode */
69			cp = kern_envp;
70			while (cp) {
71				if (strncmp(cp, "hint.", 5) == 0) {
72					cp = NULL;
73					hintp = kern_envp;
74					break;
75				}
76				while (*cp != '\0')
77					cp++;
78				cp++;
79				if (*cp == '\0') {
80					cp = NULL;
81					hintp = static_hints;
82					break;
83				}
84			}
85			break;
86		default:
87			break;
88		}
89		if (hintp == NULL)
90			hintp = kern_envp;
91	}
92
93	cp = hintp;
94	while (cp) {
95		hit = 1;
96		(*line)++;
97		if (strncmp(cp, "hint.", 5) != 0)
98			hit = 0;
99		else
100			n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s",
101			    r_name, &r_unit, r_resname, r_value);
102		if (hit && n != 4) {
103			printf("CONFIG: invalid hint '%s'\n", cp);
104			/* XXX: abuse bogus index() declaration */
105			p = index(cp, 'h');
106			*p = 'H';
107			hit = 0;
108		}
109		if (hit && startln && *startln >= 0 && *line < *startln)
110			hit = 0;
111		if (hit && name && strcmp(name, r_name) != 0)
112			hit = 0;
113		if (hit && unit && *unit != r_unit)
114			hit = 0;
115		if (hit && resname && strcmp(resname, r_resname) != 0)
116			hit = 0;
117		if (hit && value && strcmp(value, r_value) != 0)
118			hit = 0;
119		if (hit)
120			break;
121		while (*cp != '\0')
122			cp++;
123		cp++;
124		if (*cp == '\0') {
125			cp = NULL;
126			break;
127		}
128	}
129	if (cp == NULL)
130		return ENOENT;
131
132	s = cp;
133	/* This is a bit of a hack, but at least is reentrant */
134	/* Note that it returns some !unterminated! strings. */
135	s = index(s, '.') + 1;		/* start of device */
136	if (ret_name)
137		*ret_name = s;
138	s = index(s, '.') + 1;		/* start of unit */
139	if (ret_namelen)
140		*ret_namelen = s - *ret_name - 1; /* device length */
141	if (ret_unit)
142		*ret_unit = r_unit;
143	s = index(s, '.') + 1;		/* start of resname */
144	if (ret_resname)
145		*ret_resname = s;
146	s = index(s, '=') + 1;		/* start of value */
147	if (ret_resnamelen)
148		*ret_resnamelen = s - *ret_resname - 1; /* value len */
149	if (ret_value)
150		*ret_value = s;
151	if (startln)			/* line number for anchor */
152		*startln = *line + 1;
153	return 0;
154}
155
156/*
157 * Search all the data sources for matches to our query.  We look for
158 * dynamic hints first as overrides for static or fallback hints.
159 */
160static int
161resource_find(int *line, int *startln,
162    const char *name, int *unit, const char *resname, const char *value,
163    const char **ret_name, int *ret_namelen, int *ret_unit,
164    const char **ret_resname, int *ret_resnamelen, const char **ret_value)
165{
166	int i;
167	int un;
168
169	*line = 0;
170
171	/* Search for exact unit matches first */
172	i = res_find(line, startln, name, unit, resname, value,
173	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
174	    ret_value);
175	if (i == 0)
176		return 0;
177	if (unit == NULL)
178		return ENOENT;
179	/* If we are still here, search for wildcard matches */
180	un = -1;
181	i = res_find(line, startln, name, &un, resname, value,
182	    ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen,
183	    ret_value);
184	if (i == 0)
185		return 0;
186	return ENOENT;
187}
188
189int
190resource_int_value(const char *name, int unit, const char *resname, int *result)
191{
192	int error;
193	const char *str;
194	char *op;
195	unsigned long val;
196	int line;
197
198	line = 0;
199	error = resource_find(&line, NULL, name, &unit, resname, NULL,
200	    NULL, NULL, NULL, NULL, NULL, &str);
201	if (error)
202		return error;
203	if (*str == '\0')
204		return EFTYPE;
205	val = strtoul(str, &op, 0);
206	if (*op != '\0')
207		return EFTYPE;
208	*result = val;
209	return 0;
210}
211
212int
213resource_long_value(const char *name, int unit, const char *resname,
214    long *result)
215{
216	int error;
217	const char *str;
218	char *op;
219	unsigned long val;
220	int line;
221
222	line = 0;
223	error = resource_find(&line, NULL, name, &unit, resname, NULL,
224	    NULL, NULL, NULL, NULL, NULL, &str);
225	if (error)
226		return error;
227	if (*str == '\0')
228		return EFTYPE;
229	val = strtoul(str, &op, 0);
230	if (*op != '\0')
231		return EFTYPE;
232	*result = val;
233	return 0;
234}
235
236int
237resource_string_value(const char *name, int unit, const char *resname,
238    const char **result)
239{
240	int error;
241	const char *str;
242	int line;
243
244	line = 0;
245	error = resource_find(&line, NULL, name, &unit, resname, NULL,
246	    NULL, NULL, NULL, NULL, NULL, &str);
247	if (error)
248		return error;
249	*result = str;
250	return 0;
251}
252
253/*
254 * This is a bit nasty, but allows us to not modify the env strings.
255 */
256static const char *
257resource_string_copy(const char *s, int len)
258{
259	static char stringbuf[256];
260	static int offset = 0;
261	const char *ret;
262
263	if (len == 0)
264		len = strlen(s);
265	if (len > 255)
266		return NULL;
267	if ((offset + len + 1) > 255)
268		offset = 0;
269	bcopy(s, &stringbuf[offset], len);
270	stringbuf[offset + len] = '\0';
271	ret = &stringbuf[offset];
272	offset += len + 1;
273	return ret;
274}
275
276/*
277 * err = resource_find_at(&anchor, &name, &unit, resname, value)
278 * Iteratively fetch a list of devices wired "at" something
279 * res and value are restrictions.  eg: "at", "scbus0".
280 * For practical purposes, res = required, value = optional.
281 * *name and *unit are set.
282 * set *anchor to zero before starting.
283 */
284int
285resource_find_match(int *anchor, const char **name, int *unit,
286    const char *resname, const char *value)
287{
288	const char *found_name;
289	int found_namelen;
290	int found_unit;
291	int ret;
292	int newln;
293
294	newln = *anchor;
295	ret = resource_find(anchor, &newln, NULL, NULL, resname, value,
296	    &found_name, &found_namelen, &found_unit, NULL, NULL, NULL);
297	if (ret == 0) {
298		*name = resource_string_copy(found_name, found_namelen);
299		*unit = found_unit;
300	}
301	*anchor = newln;
302	return ret;
303}
304
305
306/*
307 * err = resource_find_dev(&anchor, name, &unit, res, value);
308 * Iterate through a list of devices, returning their unit numbers.
309 * res and value are optional restrictions.  eg: "at", "scbus0".
310 * *unit is set to the value.
311 * set *anchor to zero before starting.
312 */
313int
314resource_find_dev(int *anchor, const char *name, int *unit,
315    const char *resname, const char *value)
316{
317	int found_unit;
318	int newln;
319	int ret;
320
321	newln = *anchor;
322	ret = resource_find(anchor, &newln, name, NULL, resname, value,
323	    NULL, NULL, &found_unit, NULL, NULL, NULL);
324	if (ret == 0) {
325		*unit = found_unit;
326	}
327	*anchor = newln;
328	return ret;
329}
330