xref: /freebsd/lib/lib80211/lib80211_regdomain.c (revision 36ea5759aa7ab12082cfb112cff56fa2d22a1fb9)
1127be1a7SAdrian Chadd /*-
2127be1a7SAdrian Chadd  * Copyright (c) 2008 Sam Leffler, Errno Consulting
3127be1a7SAdrian Chadd  * All rights reserved.
4127be1a7SAdrian Chadd  *
5127be1a7SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
6127be1a7SAdrian Chadd  * modification, are permitted provided that the following conditions
7127be1a7SAdrian Chadd  * are met:
8127be1a7SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
9127be1a7SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
10127be1a7SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
11127be1a7SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
12127be1a7SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
13127be1a7SAdrian Chadd  *
14127be1a7SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15127be1a7SAdrian Chadd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16127be1a7SAdrian Chadd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17127be1a7SAdrian Chadd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18127be1a7SAdrian Chadd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19127be1a7SAdrian Chadd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20127be1a7SAdrian Chadd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21127be1a7SAdrian Chadd  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22127be1a7SAdrian Chadd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23127be1a7SAdrian Chadd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24127be1a7SAdrian Chadd  */
25127be1a7SAdrian Chadd #ifndef lint
26127be1a7SAdrian Chadd static const char rcsid[] = "$FreeBSD$";
27127be1a7SAdrian Chadd #endif /* not lint */
28127be1a7SAdrian Chadd 
29127be1a7SAdrian Chadd #include <sys/types.h>
30127be1a7SAdrian Chadd #include <sys/errno.h>
31127be1a7SAdrian Chadd #include <sys/param.h>
32127be1a7SAdrian Chadd #include <sys/mman.h>
33127be1a7SAdrian Chadd #include <sys/sbuf.h>
34127be1a7SAdrian Chadd #include <sys/stat.h>
35127be1a7SAdrian Chadd 
36127be1a7SAdrian Chadd #include <stdio.h>
37127be1a7SAdrian Chadd #include <string.h>
38127be1a7SAdrian Chadd #include <ctype.h>
39127be1a7SAdrian Chadd #include <fcntl.h>
40127be1a7SAdrian Chadd #include <err.h>
41127be1a7SAdrian Chadd #include <unistd.h>
42127be1a7SAdrian Chadd 
43127be1a7SAdrian Chadd #include <bsdxml.h>
44127be1a7SAdrian Chadd 
45127be1a7SAdrian Chadd #include "lib80211_regdomain.h"
46127be1a7SAdrian Chadd 
47127be1a7SAdrian Chadd #include <net80211/_ieee80211.h>
48127be1a7SAdrian Chadd 
49127be1a7SAdrian Chadd #define	MAXLEVEL	20
50127be1a7SAdrian Chadd 
51127be1a7SAdrian Chadd struct mystate {
52127be1a7SAdrian Chadd 	XML_Parser		parser;
53127be1a7SAdrian Chadd 	struct regdata		*rdp;
54127be1a7SAdrian Chadd 	struct regdomain	*rd;		/* current domain */
55127be1a7SAdrian Chadd 	struct netband		*netband;	/* current netband */
56127be1a7SAdrian Chadd 	struct freqband		*freqband;	/* current freqband */
57127be1a7SAdrian Chadd 	struct country		*country;	/* current country */
58127be1a7SAdrian Chadd 	netband_head		*curband;	/* current netband list */
59127be1a7SAdrian Chadd 	int			level;
60127be1a7SAdrian Chadd 	struct sbuf		*sbuf[MAXLEVEL];
61127be1a7SAdrian Chadd 	int			nident;
62127be1a7SAdrian Chadd };
63127be1a7SAdrian Chadd 
64127be1a7SAdrian Chadd struct ident {
65127be1a7SAdrian Chadd 	const void *id;
66127be1a7SAdrian Chadd 	void *p;
67127be1a7SAdrian Chadd 	enum { DOMAIN, COUNTRY, FREQBAND } type;
68127be1a7SAdrian Chadd };
69127be1a7SAdrian Chadd 
70127be1a7SAdrian Chadd static void
71127be1a7SAdrian Chadd start_element(void *data, const char *name, const char **attr)
72127be1a7SAdrian Chadd {
73127be1a7SAdrian Chadd #define	iseq(a,b)	(strcasecmp(a,b) == 0)
74127be1a7SAdrian Chadd 	struct mystate *mt;
75127be1a7SAdrian Chadd 	const void *id, *ref, *mode;
76127be1a7SAdrian Chadd 	int i;
77127be1a7SAdrian Chadd 
78127be1a7SAdrian Chadd 	mt = data;
79127be1a7SAdrian Chadd 	if (++mt->level == MAXLEVEL) {
80127be1a7SAdrian Chadd 		/* XXX force parser to abort */
81127be1a7SAdrian Chadd 		return;
82127be1a7SAdrian Chadd 	}
83127be1a7SAdrian Chadd 	mt->sbuf[mt->level] = sbuf_new_auto();
84127be1a7SAdrian Chadd 	id = ref = mode = NULL;
85127be1a7SAdrian Chadd 	for (i = 0; attr[i] != NULL; i += 2) {
86127be1a7SAdrian Chadd 		if (iseq(attr[i], "id")) {
87127be1a7SAdrian Chadd 			id = attr[i+1];
88127be1a7SAdrian Chadd 		} else if (iseq(attr[i], "ref")) {
89127be1a7SAdrian Chadd 			ref = attr[i+1];
90127be1a7SAdrian Chadd 		} else if (iseq(attr[i], "mode")) {
91127be1a7SAdrian Chadd 			mode = attr[i+1];
92127be1a7SAdrian Chadd 		} else
93127be1a7SAdrian Chadd 			printf("%*.*s[%s = %s]\n", mt->level + 1,
94127be1a7SAdrian Chadd 			    mt->level + 1, "", attr[i], attr[i+1]);
95127be1a7SAdrian Chadd 	}
96127be1a7SAdrian Chadd 	if (iseq(name, "rd") && mt->rd == NULL) {
97127be1a7SAdrian Chadd 		if (mt->country == NULL) {
98127be1a7SAdrian Chadd 			mt->rd = calloc(1, sizeof(struct regdomain));
99127be1a7SAdrian Chadd 			mt->rd->name = strdup(id);
100127be1a7SAdrian Chadd 			mt->nident++;
101127be1a7SAdrian Chadd 			LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
102127be1a7SAdrian Chadd 		} else
103127be1a7SAdrian Chadd 			mt->country->rd = (void *)strdup(ref);
104127be1a7SAdrian Chadd 		return;
105127be1a7SAdrian Chadd 	}
106127be1a7SAdrian Chadd 	if (iseq(name, "defcc") && mt->rd != NULL) {
107127be1a7SAdrian Chadd 		mt->rd->cc = (void *)strdup(ref);
108127be1a7SAdrian Chadd 		return;
109127be1a7SAdrian Chadd 	}
110127be1a7SAdrian Chadd 	if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
111127be1a7SAdrian Chadd 		if (mode == NULL) {
112127be1a7SAdrian Chadd 			warnx("no mode for netband at line %ld",
113127be1a7SAdrian Chadd 			    XML_GetCurrentLineNumber(mt->parser));
114127be1a7SAdrian Chadd 			return;
115127be1a7SAdrian Chadd 		}
116127be1a7SAdrian Chadd 		if (iseq(mode, "11b"))
117127be1a7SAdrian Chadd 			mt->curband = &mt->rd->bands_11b;
118127be1a7SAdrian Chadd 		else if (iseq(mode, "11g"))
119127be1a7SAdrian Chadd 			mt->curband = &mt->rd->bands_11g;
120127be1a7SAdrian Chadd 		else if (iseq(mode, "11a"))
121127be1a7SAdrian Chadd 			mt->curband = &mt->rd->bands_11a;
122127be1a7SAdrian Chadd 		else if (iseq(mode, "11ng"))
123127be1a7SAdrian Chadd 			mt->curband = &mt->rd->bands_11ng;
124127be1a7SAdrian Chadd 		else if (iseq(mode, "11na"))
125127be1a7SAdrian Chadd 			mt->curband = &mt->rd->bands_11na;
126*36ea5759SAdrian Chadd 		else if (iseq(mode, "11ac"))
127*36ea5759SAdrian Chadd 			mt->curband = &mt->rd->bands_11ac;
128*36ea5759SAdrian Chadd 		else if (iseq(mode, "11acg"))
129*36ea5759SAdrian Chadd 			mt->curband = &mt->rd->bands_11acg;
130127be1a7SAdrian Chadd 		else
131127be1a7SAdrian Chadd 			warnx("unknown mode \"%s\" at line %ld",
132127be1a7SAdrian Chadd 			    __DECONST(char *, mode),
133127be1a7SAdrian Chadd 			    XML_GetCurrentLineNumber(mt->parser));
134127be1a7SAdrian Chadd 		return;
135127be1a7SAdrian Chadd 	}
136127be1a7SAdrian Chadd 	if (iseq(name, "band") && mt->netband == NULL) {
137127be1a7SAdrian Chadd 		if (mt->curband == NULL) {
138127be1a7SAdrian Chadd 			warnx("band without enclosing netband at line %ld",
139127be1a7SAdrian Chadd 			    XML_GetCurrentLineNumber(mt->parser));
140127be1a7SAdrian Chadd 			return;
141127be1a7SAdrian Chadd 		}
142127be1a7SAdrian Chadd 		mt->netband = calloc(1, sizeof(struct netband));
143127be1a7SAdrian Chadd 		LIST_INSERT_HEAD(mt->curband, mt->netband, next);
144127be1a7SAdrian Chadd 		return;
145127be1a7SAdrian Chadd 	}
146127be1a7SAdrian Chadd 	if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
147127be1a7SAdrian Chadd 		/* XXX handle inlines and merge into table? */
148127be1a7SAdrian Chadd 		if (mt->netband->band != NULL) {
149127be1a7SAdrian Chadd 			warnx("duplicate freqband at line %ld ignored",
150127be1a7SAdrian Chadd 			    XML_GetCurrentLineNumber(mt->parser));
151127be1a7SAdrian Chadd 			/* XXX complain */
152127be1a7SAdrian Chadd 		} else
153127be1a7SAdrian Chadd 			mt->netband->band = (void *)strdup(ref);
154127be1a7SAdrian Chadd 		return;
155127be1a7SAdrian Chadd 	}
156127be1a7SAdrian Chadd 
157127be1a7SAdrian Chadd 	if (iseq(name, "country") && mt->country == NULL) {
158127be1a7SAdrian Chadd 		mt->country = calloc(1, sizeof(struct country));
159127be1a7SAdrian Chadd 		mt->country->isoname = strdup(id);
160127be1a7SAdrian Chadd 		mt->country->code = NO_COUNTRY;
161127be1a7SAdrian Chadd 		mt->nident++;
162127be1a7SAdrian Chadd 		LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
163127be1a7SAdrian Chadd 		return;
164127be1a7SAdrian Chadd 	}
165127be1a7SAdrian Chadd 
166127be1a7SAdrian Chadd 	if (iseq(name, "freqband") && mt->freqband == NULL) {
167127be1a7SAdrian Chadd 		mt->freqband = calloc(1, sizeof(struct freqband));
168127be1a7SAdrian Chadd 		mt->freqband->id = strdup(id);
169127be1a7SAdrian Chadd 		mt->nident++;
170127be1a7SAdrian Chadd 		LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
171127be1a7SAdrian Chadd 		return;
172127be1a7SAdrian Chadd 	}
173127be1a7SAdrian Chadd #undef iseq
174127be1a7SAdrian Chadd }
175127be1a7SAdrian Chadd 
176127be1a7SAdrian Chadd static int
177127be1a7SAdrian Chadd decode_flag(struct mystate *mt, const char *p, int len)
178127be1a7SAdrian Chadd {
179127be1a7SAdrian Chadd #define	iseq(a,b)	(strcasecmp(a,b) == 0)
180127be1a7SAdrian Chadd 	static const struct {
181127be1a7SAdrian Chadd 		const char *name;
182127be1a7SAdrian Chadd 		int len;
183127be1a7SAdrian Chadd 		uint32_t value;
184127be1a7SAdrian Chadd 	} flags[] = {
185127be1a7SAdrian Chadd #define	FLAG(x)	{ #x, sizeof(#x)-1, x }
186127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_A),
187127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_B),
188127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_G),
189127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_HT20),
190127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_HT40),
191*36ea5759SAdrian Chadd 		FLAG(IEEE80211_CHAN_VHT20),
192*36ea5759SAdrian Chadd 		FLAG(IEEE80211_CHAN_VHT40),
193*36ea5759SAdrian Chadd 		FLAG(IEEE80211_CHAN_VHT80),
194*36ea5759SAdrian Chadd 		/*
195*36ea5759SAdrian Chadd 		 * XXX VHT80_80? This likely should be done by
196*36ea5759SAdrian Chadd 		 * 80MHz chan logic in net80211 / ifconfig.
197*36ea5759SAdrian Chadd 		 */
198*36ea5759SAdrian Chadd 		FLAG(IEEE80211_CHAN_VHT160),
199127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_ST),
200127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_TURBO),
201127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_PASSIVE),
202127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_DFS),
203127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_CCK),
204127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_OFDM),
205127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_2GHZ),
206127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_5GHZ),
207127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_DYN),
208127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_GFSK),
209127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_GSM),
210127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_STURBO),
211127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_HALF),
212127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_QUARTER),
213127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_HT40U),
214127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_HT40D),
215127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_4MSXMIT),
216127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_NOADHOC),
217127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_NOHOSTAP),
218127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_11D),
219127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_FHSS),
220127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_PUREG),
221127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_108A),
222127be1a7SAdrian Chadd 		FLAG(IEEE80211_CHAN_108G),
223127be1a7SAdrian Chadd #undef FLAG
224127be1a7SAdrian Chadd 		{ "ECM",	3,	REQ_ECM },
225127be1a7SAdrian Chadd 		{ "INDOOR",	6,	REQ_INDOOR },
226127be1a7SAdrian Chadd 		{ "OUTDOOR",	7,	REQ_OUTDOOR },
227127be1a7SAdrian Chadd 	};
228127be1a7SAdrian Chadd 	unsigned int i;
229127be1a7SAdrian Chadd 
230127be1a7SAdrian Chadd 	for (i = 0; i < nitems(flags); i++)
231127be1a7SAdrian Chadd 		if (len == flags[i].len && iseq(p, flags[i].name))
232127be1a7SAdrian Chadd 			return flags[i].value;
233127be1a7SAdrian Chadd 	warnx("unknown flag \"%.*s\" at line %ld ignored",
234127be1a7SAdrian Chadd 	    len, p, XML_GetCurrentLineNumber(mt->parser));
235127be1a7SAdrian Chadd 	return 0;
236127be1a7SAdrian Chadd #undef iseq
237127be1a7SAdrian Chadd }
238127be1a7SAdrian Chadd 
239127be1a7SAdrian Chadd static void
240127be1a7SAdrian Chadd end_element(void *data, const char *name)
241127be1a7SAdrian Chadd {
242127be1a7SAdrian Chadd #define	iseq(a,b)	(strcasecmp(a,b) == 0)
243127be1a7SAdrian Chadd 	struct mystate *mt;
244127be1a7SAdrian Chadd 	int len;
245127be1a7SAdrian Chadd 	char *p;
246127be1a7SAdrian Chadd 
247127be1a7SAdrian Chadd 	mt = data;
248127be1a7SAdrian Chadd 	sbuf_finish(mt->sbuf[mt->level]);
249127be1a7SAdrian Chadd 	p = sbuf_data(mt->sbuf[mt->level]);
250127be1a7SAdrian Chadd 	len = sbuf_len(mt->sbuf[mt->level]);
251127be1a7SAdrian Chadd 
252127be1a7SAdrian Chadd 	/* <freqband>...</freqband> */
253127be1a7SAdrian Chadd 	if (iseq(name, "freqstart") && mt->freqband != NULL) {
254127be1a7SAdrian Chadd 		mt->freqband->freqStart = strtoul(p, NULL, 0);
255127be1a7SAdrian Chadd 		goto done;
256127be1a7SAdrian Chadd 	}
257127be1a7SAdrian Chadd 	if (iseq(name, "freqend") && mt->freqband != NULL) {
258127be1a7SAdrian Chadd 		mt->freqband->freqEnd = strtoul(p, NULL, 0);
259127be1a7SAdrian Chadd 		goto done;
260127be1a7SAdrian Chadd 	}
261127be1a7SAdrian Chadd 	if (iseq(name, "chanwidth") && mt->freqband != NULL) {
262127be1a7SAdrian Chadd 		mt->freqband->chanWidth = strtoul(p, NULL, 0);
263127be1a7SAdrian Chadd 		goto done;
264127be1a7SAdrian Chadd 	}
265127be1a7SAdrian Chadd 	if (iseq(name, "chansep") && mt->freqband != NULL) {
266127be1a7SAdrian Chadd 		mt->freqband->chanSep = strtoul(p, NULL, 0);
267127be1a7SAdrian Chadd 		goto done;
268127be1a7SAdrian Chadd 	}
269127be1a7SAdrian Chadd 	if (iseq(name, "flags")) {
270127be1a7SAdrian Chadd 		if (mt->freqband != NULL)
271127be1a7SAdrian Chadd 			mt->freqband->flags |= decode_flag(mt, p, len);
272127be1a7SAdrian Chadd 		else if (mt->netband != NULL)
273127be1a7SAdrian Chadd 			mt->netband->flags |= decode_flag(mt, p, len);
274127be1a7SAdrian Chadd 		else {
275127be1a7SAdrian Chadd 			warnx("flags without freqband or netband at line %ld ignored",
276127be1a7SAdrian Chadd 			    XML_GetCurrentLineNumber(mt->parser));
277127be1a7SAdrian Chadd 		}
278127be1a7SAdrian Chadd 		goto done;
279127be1a7SAdrian Chadd 	}
280127be1a7SAdrian Chadd 
281127be1a7SAdrian Chadd 	/* <rd> ... </rd> */
282127be1a7SAdrian Chadd 	if (iseq(name, "name") && mt->rd != NULL) {
283127be1a7SAdrian Chadd 		mt->rd->name = strdup(p);
284127be1a7SAdrian Chadd 		goto done;
285127be1a7SAdrian Chadd 	}
286127be1a7SAdrian Chadd 	if (iseq(name, "sku") && mt->rd != NULL) {
287127be1a7SAdrian Chadd 		mt->rd->sku = strtoul(p, NULL, 0);
288127be1a7SAdrian Chadd 		goto done;
289127be1a7SAdrian Chadd 	}
290127be1a7SAdrian Chadd 	if (iseq(name, "netband") && mt->rd != NULL) {
291127be1a7SAdrian Chadd 		mt->curband = NULL;
292127be1a7SAdrian Chadd 		goto done;
293127be1a7SAdrian Chadd 	}
294127be1a7SAdrian Chadd 
295127be1a7SAdrian Chadd 	/* <band> ... </band> */
296127be1a7SAdrian Chadd 	if (iseq(name, "freqband") && mt->netband != NULL) {
297127be1a7SAdrian Chadd 		/* XXX handle inline freqbands */
298127be1a7SAdrian Chadd 		goto done;
299127be1a7SAdrian Chadd 	}
300127be1a7SAdrian Chadd 	if (iseq(name, "maxpower") && mt->netband != NULL) {
301127be1a7SAdrian Chadd 		mt->netband->maxPower = strtoul(p, NULL, 0);
302127be1a7SAdrian Chadd 		goto done;
303127be1a7SAdrian Chadd 	}
304127be1a7SAdrian Chadd 	if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
305127be1a7SAdrian Chadd 		mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
306127be1a7SAdrian Chadd 		goto done;
307127be1a7SAdrian Chadd 	}
308127be1a7SAdrian Chadd 	if (iseq(name, "maxantgain") && mt->netband != NULL) {
309127be1a7SAdrian Chadd 		mt->netband->maxAntGain = strtoul(p, NULL, 0);
310127be1a7SAdrian Chadd 		goto done;
311127be1a7SAdrian Chadd 	}
312127be1a7SAdrian Chadd 
313127be1a7SAdrian Chadd 	/* <country>...</country> */
314127be1a7SAdrian Chadd 	if (iseq(name, "isocc") && mt->country != NULL) {
315127be1a7SAdrian Chadd 		mt->country->code = strtoul(p, NULL, 0);
316127be1a7SAdrian Chadd 		goto done;
317127be1a7SAdrian Chadd 	}
318127be1a7SAdrian Chadd 	if (iseq(name, "name") && mt->country != NULL) {
319127be1a7SAdrian Chadd 		mt->country->name = strdup(p);
320127be1a7SAdrian Chadd 		goto done;
321127be1a7SAdrian Chadd 	}
322127be1a7SAdrian Chadd 
323127be1a7SAdrian Chadd 	if (len != 0) {
324127be1a7SAdrian Chadd 		warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
325127be1a7SAdrian Chadd 		    name, p, XML_GetCurrentLineNumber(mt->parser));
326127be1a7SAdrian Chadd 		/* XXX goto done? */
327127be1a7SAdrian Chadd 	}
328127be1a7SAdrian Chadd 	/* </freqband> */
329127be1a7SAdrian Chadd 	if (iseq(name, "freqband") && mt->freqband != NULL) {
330127be1a7SAdrian Chadd 		/* XXX must have start/end frequencies */
331127be1a7SAdrian Chadd 		/* XXX must have channel width/sep */
332127be1a7SAdrian Chadd 		mt->freqband = NULL;
333127be1a7SAdrian Chadd 		goto done;
334127be1a7SAdrian Chadd 	}
335127be1a7SAdrian Chadd 	/* </rd> */
336127be1a7SAdrian Chadd 	if (iseq(name, "rd") && mt->rd != NULL) {
337127be1a7SAdrian Chadd 		mt->rd = NULL;
338127be1a7SAdrian Chadd 		goto done;
339127be1a7SAdrian Chadd 	}
340127be1a7SAdrian Chadd 	/* </band> */
341127be1a7SAdrian Chadd 	if (iseq(name, "band") && mt->netband != NULL) {
342127be1a7SAdrian Chadd 		if (mt->netband->band == NULL) {
343127be1a7SAdrian Chadd 			warnx("no freqbands for band at line %ld",
344127be1a7SAdrian Chadd 			   XML_GetCurrentLineNumber(mt->parser));
345127be1a7SAdrian Chadd 		}
346127be1a7SAdrian Chadd 		if (mt->netband->maxPower == 0) {
347127be1a7SAdrian Chadd 			warnx("no maxpower for band at line %ld",
348127be1a7SAdrian Chadd 			   XML_GetCurrentLineNumber(mt->parser));
349127be1a7SAdrian Chadd 		}
350127be1a7SAdrian Chadd 		/* default max power w/ DFS to max power */
351127be1a7SAdrian Chadd 		if (mt->netband->maxPowerDFS == 0)
352127be1a7SAdrian Chadd 			mt->netband->maxPowerDFS = mt->netband->maxPower;
353127be1a7SAdrian Chadd 		mt->netband = NULL;
354127be1a7SAdrian Chadd 		goto done;
355127be1a7SAdrian Chadd 	}
356127be1a7SAdrian Chadd 	/* </netband> */
357127be1a7SAdrian Chadd 	if (iseq(name, "netband") && mt->netband != NULL) {
358127be1a7SAdrian Chadd 		mt->curband = NULL;
359127be1a7SAdrian Chadd 		goto done;
360127be1a7SAdrian Chadd 	}
361127be1a7SAdrian Chadd 	/* </country> */
362127be1a7SAdrian Chadd 	if (iseq(name, "country") && mt->country != NULL) {
363127be1a7SAdrian Chadd 		/* XXX NO_COUNTRY should be in the net80211 country enum */
364127be1a7SAdrian Chadd 		if ((int) mt->country->code == NO_COUNTRY) {
365127be1a7SAdrian Chadd 			warnx("no ISO cc for country at line %ld",
366127be1a7SAdrian Chadd 			   XML_GetCurrentLineNumber(mt->parser));
367127be1a7SAdrian Chadd 		}
368127be1a7SAdrian Chadd 		if (mt->country->name == NULL) {
369127be1a7SAdrian Chadd 			warnx("no name for country at line %ld",
370127be1a7SAdrian Chadd 			   XML_GetCurrentLineNumber(mt->parser));
371127be1a7SAdrian Chadd 		}
372127be1a7SAdrian Chadd 		if (mt->country->rd == NULL) {
373127be1a7SAdrian Chadd 			warnx("no regdomain reference for country at line %ld",
374127be1a7SAdrian Chadd 			   XML_GetCurrentLineNumber(mt->parser));
375127be1a7SAdrian Chadd 		}
376127be1a7SAdrian Chadd 		mt->country = NULL;
377127be1a7SAdrian Chadd 		goto done;
378127be1a7SAdrian Chadd 	}
379127be1a7SAdrian Chadd done:
380127be1a7SAdrian Chadd 	sbuf_delete(mt->sbuf[mt->level]);
381127be1a7SAdrian Chadd 	mt->sbuf[mt->level--] = NULL;
382127be1a7SAdrian Chadd #undef iseq
383127be1a7SAdrian Chadd }
384127be1a7SAdrian Chadd 
385127be1a7SAdrian Chadd static void
386127be1a7SAdrian Chadd char_data(void *data, const XML_Char *s, int len)
387127be1a7SAdrian Chadd {
388127be1a7SAdrian Chadd 	struct mystate *mt;
389127be1a7SAdrian Chadd 	const char *b, *e;
390127be1a7SAdrian Chadd 
391127be1a7SAdrian Chadd 	mt = data;
392127be1a7SAdrian Chadd 
393127be1a7SAdrian Chadd 	b = s;
394127be1a7SAdrian Chadd 	e = s + len-1;
395127be1a7SAdrian Chadd 	for (; isspace(*b) && b < e; b++)
396127be1a7SAdrian Chadd 		;
397127be1a7SAdrian Chadd 	for (; isspace(*e) && e > b; e++)
398127be1a7SAdrian Chadd 		;
399127be1a7SAdrian Chadd 	if (e != b || (*b != '\0' && !isspace(*b)))
400127be1a7SAdrian Chadd 		sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
401127be1a7SAdrian Chadd }
402127be1a7SAdrian Chadd 
403127be1a7SAdrian Chadd static void *
404127be1a7SAdrian Chadd findid(struct regdata *rdp, const void *id, int type)
405127be1a7SAdrian Chadd {
406127be1a7SAdrian Chadd 	struct ident *ip;
407127be1a7SAdrian Chadd 
408127be1a7SAdrian Chadd 	for (ip = rdp->ident; ip->id != NULL; ip++)
409127be1a7SAdrian Chadd 		if ((int) ip->type == type && strcasecmp(ip->id, id) == 0)
410127be1a7SAdrian Chadd 			return ip->p;
411127be1a7SAdrian Chadd 	return NULL;
412127be1a7SAdrian Chadd }
413127be1a7SAdrian Chadd 
414127be1a7SAdrian Chadd /*
415127be1a7SAdrian Chadd  * Parse an regdomain XML configuration and build the internal representation.
416127be1a7SAdrian Chadd  */
417127be1a7SAdrian Chadd int
418127be1a7SAdrian Chadd lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
419127be1a7SAdrian Chadd {
420127be1a7SAdrian Chadd 	struct mystate *mt;
421127be1a7SAdrian Chadd 	struct regdomain *dp;
422127be1a7SAdrian Chadd 	struct country *cp;
423127be1a7SAdrian Chadd 	struct freqband *fp;
424127be1a7SAdrian Chadd 	struct netband *nb;
425127be1a7SAdrian Chadd 	const void *id;
426127be1a7SAdrian Chadd 	int i, errors;
427127be1a7SAdrian Chadd 
428127be1a7SAdrian Chadd 	memset(rdp, 0, sizeof(struct regdata));
429127be1a7SAdrian Chadd 	mt = calloc(1, sizeof(struct mystate));
430127be1a7SAdrian Chadd 	if (mt == NULL)
431127be1a7SAdrian Chadd 		return ENOMEM;
432127be1a7SAdrian Chadd 	/* parse the XML input */
433127be1a7SAdrian Chadd 	mt->rdp = rdp;
434127be1a7SAdrian Chadd 	mt->parser = XML_ParserCreate(NULL);
435127be1a7SAdrian Chadd 	XML_SetUserData(mt->parser, mt);
436127be1a7SAdrian Chadd 	XML_SetElementHandler(mt->parser, start_element, end_element);
437127be1a7SAdrian Chadd 	XML_SetCharacterDataHandler(mt->parser, char_data);
438127be1a7SAdrian Chadd 	if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
439127be1a7SAdrian Chadd 		warnx("%s: %s at line %ld", __func__,
440127be1a7SAdrian Chadd 		   XML_ErrorString(XML_GetErrorCode(mt->parser)),
441127be1a7SAdrian Chadd 		   XML_GetCurrentLineNumber(mt->parser));
442127be1a7SAdrian Chadd 		return -1;
443127be1a7SAdrian Chadd 	}
444127be1a7SAdrian Chadd 	XML_ParserFree(mt->parser);
445127be1a7SAdrian Chadd 
446127be1a7SAdrian Chadd 	/* setup the identifer table */
447127be1a7SAdrian Chadd 	rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
448127be1a7SAdrian Chadd 	if (rdp->ident == NULL)
449127be1a7SAdrian Chadd 		return ENOMEM;
450127be1a7SAdrian Chadd 	free(mt);
451127be1a7SAdrian Chadd 
452127be1a7SAdrian Chadd 	errors = 0;
453127be1a7SAdrian Chadd 	i = 0;
454127be1a7SAdrian Chadd 	LIST_FOREACH(dp, &rdp->domains, next) {
455127be1a7SAdrian Chadd 		rdp->ident[i].id = dp->name;
456127be1a7SAdrian Chadd 		rdp->ident[i].p = dp;
457127be1a7SAdrian Chadd 		rdp->ident[i].type = DOMAIN;
458127be1a7SAdrian Chadd 		i++;
459127be1a7SAdrian Chadd 	}
460127be1a7SAdrian Chadd 	LIST_FOREACH(fp, &rdp->freqbands, next) {
461127be1a7SAdrian Chadd 		rdp->ident[i].id = fp->id;
462127be1a7SAdrian Chadd 		rdp->ident[i].p = fp;
463127be1a7SAdrian Chadd 		rdp->ident[i].type = FREQBAND;
464127be1a7SAdrian Chadd 		i++;
465127be1a7SAdrian Chadd 	}
466127be1a7SAdrian Chadd 	LIST_FOREACH(cp, &rdp->countries, next) {
467127be1a7SAdrian Chadd 		rdp->ident[i].id = cp->isoname;
468127be1a7SAdrian Chadd 		rdp->ident[i].p = cp;
469127be1a7SAdrian Chadd 		rdp->ident[i].type = COUNTRY;
470127be1a7SAdrian Chadd 		i++;
471127be1a7SAdrian Chadd 	}
472127be1a7SAdrian Chadd 
473127be1a7SAdrian Chadd 	/* patch references */
474127be1a7SAdrian Chadd 	LIST_FOREACH(dp, &rdp->domains, next) {
475127be1a7SAdrian Chadd 		if (dp->cc != NULL) {
476127be1a7SAdrian Chadd 			id = dp->cc;
477127be1a7SAdrian Chadd 			dp->cc = findid(rdp, id, COUNTRY);
478127be1a7SAdrian Chadd 			if (dp->cc == NULL) {
479127be1a7SAdrian Chadd 				warnx("undefined country \"%s\"",
480127be1a7SAdrian Chadd 				    __DECONST(char *, id));
481127be1a7SAdrian Chadd 				errors++;
482127be1a7SAdrian Chadd 			}
483127be1a7SAdrian Chadd 			free(__DECONST(char *, id));
484127be1a7SAdrian Chadd 		}
485127be1a7SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11b, next) {
486127be1a7SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
487127be1a7SAdrian Chadd 			if (id == NULL) {
488127be1a7SAdrian Chadd 				warnx("undefined 11b band \"%s\"",
489127be1a7SAdrian Chadd 				    __DECONST(char *, nb->band));
490127be1a7SAdrian Chadd 				errors++;
491127be1a7SAdrian Chadd 			}
492127be1a7SAdrian Chadd 			nb->band = id;
493127be1a7SAdrian Chadd 		}
494127be1a7SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11g, next) {
495127be1a7SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
496127be1a7SAdrian Chadd 			if (id == NULL) {
497127be1a7SAdrian Chadd 				warnx("undefined 11g band \"%s\"",
498127be1a7SAdrian Chadd 				    __DECONST(char *, nb->band));
499127be1a7SAdrian Chadd 				errors++;
500127be1a7SAdrian Chadd 			}
501127be1a7SAdrian Chadd 			nb->band = id;
502127be1a7SAdrian Chadd 		}
503127be1a7SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11a, next) {
504127be1a7SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
505127be1a7SAdrian Chadd 			if (id == NULL) {
506127be1a7SAdrian Chadd 				warnx("undefined 11a band \"%s\"",
507127be1a7SAdrian Chadd 				    __DECONST(char *, nb->band));
508127be1a7SAdrian Chadd 				errors++;
509127be1a7SAdrian Chadd 			}
510127be1a7SAdrian Chadd 			nb->band = id;
511127be1a7SAdrian Chadd 		}
512127be1a7SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11ng, next) {
513127be1a7SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
514127be1a7SAdrian Chadd 			if (id == NULL) {
515127be1a7SAdrian Chadd 				warnx("undefined 11ng band \"%s\"",
516127be1a7SAdrian Chadd 				    __DECONST(char *, nb->band));
517127be1a7SAdrian Chadd 				errors++;
518127be1a7SAdrian Chadd 			}
519127be1a7SAdrian Chadd 			nb->band = id;
520127be1a7SAdrian Chadd 		}
521127be1a7SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11na, next) {
522127be1a7SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
523127be1a7SAdrian Chadd 			if (id == NULL) {
524127be1a7SAdrian Chadd 				warnx("undefined 11na band \"%s\"",
525127be1a7SAdrian Chadd 				    __DECONST(char *, nb->band));
526127be1a7SAdrian Chadd 				errors++;
527127be1a7SAdrian Chadd 			}
528127be1a7SAdrian Chadd 			nb->band = id;
529127be1a7SAdrian Chadd 		}
530*36ea5759SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11ac, next) {
531*36ea5759SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
532*36ea5759SAdrian Chadd 			if (id == NULL) {
533*36ea5759SAdrian Chadd 				warnx("undefined 11ac band \"%s\"",
534*36ea5759SAdrian Chadd 				    __DECONST(char *, nb->band));
535*36ea5759SAdrian Chadd 				errors++;
536*36ea5759SAdrian Chadd 			}
537*36ea5759SAdrian Chadd 			nb->band = id;
538*36ea5759SAdrian Chadd 		}
539*36ea5759SAdrian Chadd 		LIST_FOREACH(nb, &dp->bands_11acg, next) {
540*36ea5759SAdrian Chadd 			id = findid(rdp, nb->band, FREQBAND);
541*36ea5759SAdrian Chadd 			if (id == NULL) {
542*36ea5759SAdrian Chadd 				warnx("undefined 11acg band \"%s\"",
543*36ea5759SAdrian Chadd 				    __DECONST(char *, nb->band));
544*36ea5759SAdrian Chadd 				errors++;
545*36ea5759SAdrian Chadd 			}
546*36ea5759SAdrian Chadd 			nb->band = id;
547*36ea5759SAdrian Chadd 		}
548127be1a7SAdrian Chadd 	}
549127be1a7SAdrian Chadd 	LIST_FOREACH(cp, &rdp->countries, next) {
550127be1a7SAdrian Chadd 		id = cp->rd;
551127be1a7SAdrian Chadd 		cp->rd = findid(rdp, id, DOMAIN);
552127be1a7SAdrian Chadd 		if (cp->rd == NULL) {
553127be1a7SAdrian Chadd 			warnx("undefined country \"%s\"",
554127be1a7SAdrian Chadd 			    __DECONST(char *, id));
555127be1a7SAdrian Chadd 			errors++;
556127be1a7SAdrian Chadd 		}
557127be1a7SAdrian Chadd 		free(__DECONST(char *, id));
558127be1a7SAdrian Chadd 	}
559127be1a7SAdrian Chadd 
560127be1a7SAdrian Chadd 	return errors ? EINVAL : 0;
561127be1a7SAdrian Chadd }
562127be1a7SAdrian Chadd 
563127be1a7SAdrian Chadd static void
564127be1a7SAdrian Chadd cleanup_bands(netband_head *head)
565127be1a7SAdrian Chadd {
566127be1a7SAdrian Chadd 	struct netband *nb;
567127be1a7SAdrian Chadd 
568127be1a7SAdrian Chadd 	for (;;) {
569127be1a7SAdrian Chadd 		nb = LIST_FIRST(head);
570127be1a7SAdrian Chadd 		if (nb == NULL)
571127be1a7SAdrian Chadd 			break;
572127be1a7SAdrian Chadd 		free(nb);
573127be1a7SAdrian Chadd 	}
574127be1a7SAdrian Chadd }
575127be1a7SAdrian Chadd 
576127be1a7SAdrian Chadd /*
577127be1a7SAdrian Chadd  * Cleanup state/resources for a previously parsed regdomain database.
578127be1a7SAdrian Chadd  */
579127be1a7SAdrian Chadd void
580127be1a7SAdrian Chadd lib80211_regdomain_cleanup(struct regdata *rdp)
581127be1a7SAdrian Chadd {
582127be1a7SAdrian Chadd 
583127be1a7SAdrian Chadd 	free(rdp->ident);
584127be1a7SAdrian Chadd 	rdp->ident = NULL;
585127be1a7SAdrian Chadd 	for (;;) {
586127be1a7SAdrian Chadd 		struct regdomain *dp = LIST_FIRST(&rdp->domains);
587127be1a7SAdrian Chadd 		if (dp == NULL)
588127be1a7SAdrian Chadd 			break;
589127be1a7SAdrian Chadd 		LIST_REMOVE(dp, next);
590127be1a7SAdrian Chadd 		cleanup_bands(&dp->bands_11b);
591127be1a7SAdrian Chadd 		cleanup_bands(&dp->bands_11g);
592127be1a7SAdrian Chadd 		cleanup_bands(&dp->bands_11a);
593127be1a7SAdrian Chadd 		cleanup_bands(&dp->bands_11ng);
594127be1a7SAdrian Chadd 		cleanup_bands(&dp->bands_11na);
595*36ea5759SAdrian Chadd 		cleanup_bands(&dp->bands_11ac);
596*36ea5759SAdrian Chadd 		cleanup_bands(&dp->bands_11acg);
597127be1a7SAdrian Chadd 		if (dp->name != NULL)
598127be1a7SAdrian Chadd 			free(__DECONST(char *, dp->name));
599127be1a7SAdrian Chadd 	}
600127be1a7SAdrian Chadd 	for (;;) {
601127be1a7SAdrian Chadd 		struct country *cp = LIST_FIRST(&rdp->countries);
602127be1a7SAdrian Chadd 		if (cp == NULL)
603127be1a7SAdrian Chadd 			break;
604127be1a7SAdrian Chadd 		LIST_REMOVE(cp, next);
605127be1a7SAdrian Chadd 		if (cp->name != NULL)
606127be1a7SAdrian Chadd 			free(__DECONST(char *, cp->name));
607127be1a7SAdrian Chadd 		free(cp);
608127be1a7SAdrian Chadd 	}
609127be1a7SAdrian Chadd 	for (;;) {
610127be1a7SAdrian Chadd 		struct freqband *fp = LIST_FIRST(&rdp->freqbands);
611127be1a7SAdrian Chadd 		if (fp == NULL)
612127be1a7SAdrian Chadd 			break;
613127be1a7SAdrian Chadd 		LIST_REMOVE(fp, next);
614127be1a7SAdrian Chadd 		free(fp);
615127be1a7SAdrian Chadd 	}
616127be1a7SAdrian Chadd }
617127be1a7SAdrian Chadd 
618127be1a7SAdrian Chadd struct regdata *
619127be1a7SAdrian Chadd lib80211_alloc_regdata(void)
620127be1a7SAdrian Chadd {
621127be1a7SAdrian Chadd 	struct regdata *rdp;
622127be1a7SAdrian Chadd 	struct stat sb;
623127be1a7SAdrian Chadd 	void *xml;
624127be1a7SAdrian Chadd 	int fd;
625127be1a7SAdrian Chadd 
626127be1a7SAdrian Chadd 	rdp = calloc(1, sizeof(struct regdata));
627127be1a7SAdrian Chadd 
628127be1a7SAdrian Chadd 	fd = open(_PATH_REGDOMAIN, O_RDONLY);
629127be1a7SAdrian Chadd 	if (fd < 0) {
630127be1a7SAdrian Chadd #ifdef DEBUG
631127be1a7SAdrian Chadd 		warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
632127be1a7SAdrian Chadd #endif
633127be1a7SAdrian Chadd 		free(rdp);
634127be1a7SAdrian Chadd 		return NULL;
635127be1a7SAdrian Chadd 	}
636127be1a7SAdrian Chadd 	if (fstat(fd, &sb) < 0) {
637127be1a7SAdrian Chadd #ifdef DEBUG
638127be1a7SAdrian Chadd 		warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
639127be1a7SAdrian Chadd #endif
640127be1a7SAdrian Chadd 		close(fd);
641127be1a7SAdrian Chadd 		free(rdp);
642127be1a7SAdrian Chadd 		return NULL;
643127be1a7SAdrian Chadd 	}
644127be1a7SAdrian Chadd 	xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
645127be1a7SAdrian Chadd 	if (xml == MAP_FAILED) {
646127be1a7SAdrian Chadd #ifdef DEBUG
647127be1a7SAdrian Chadd 		warn("%s: mmap", __func__);
648127be1a7SAdrian Chadd #endif
649127be1a7SAdrian Chadd 		close(fd);
650127be1a7SAdrian Chadd 		free(rdp);
651127be1a7SAdrian Chadd 		return NULL;
652127be1a7SAdrian Chadd 	}
653127be1a7SAdrian Chadd 	if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
654127be1a7SAdrian Chadd #ifdef DEBUG
655127be1a7SAdrian Chadd 		warn("%s: error reading regulatory database", __func__);
656127be1a7SAdrian Chadd #endif
657127be1a7SAdrian Chadd 		munmap(xml, sb.st_size);
658127be1a7SAdrian Chadd 		close(fd);
659127be1a7SAdrian Chadd 		free(rdp);
660127be1a7SAdrian Chadd 		return NULL;
661127be1a7SAdrian Chadd 	}
662127be1a7SAdrian Chadd 	munmap(xml, sb.st_size);
663127be1a7SAdrian Chadd 	close(fd);
664127be1a7SAdrian Chadd 
665127be1a7SAdrian Chadd 	return rdp;
666127be1a7SAdrian Chadd }
667127be1a7SAdrian Chadd 
668127be1a7SAdrian Chadd void
669127be1a7SAdrian Chadd lib80211_free_regdata(struct regdata *rdp)
670127be1a7SAdrian Chadd {
671127be1a7SAdrian Chadd 	lib80211_regdomain_cleanup(rdp);
672127be1a7SAdrian Chadd 	free(rdp);
673127be1a7SAdrian Chadd }
674127be1a7SAdrian Chadd 
675127be1a7SAdrian Chadd /*
676127be1a7SAdrian Chadd  * Lookup a regdomain by SKU.
677127be1a7SAdrian Chadd  */
678127be1a7SAdrian Chadd const struct regdomain *
679127be1a7SAdrian Chadd lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
680127be1a7SAdrian Chadd {
681127be1a7SAdrian Chadd 	const struct regdomain *dp;
682127be1a7SAdrian Chadd 
683127be1a7SAdrian Chadd 	LIST_FOREACH(dp, &rdp->domains, next) {
684127be1a7SAdrian Chadd 		if (dp->sku == sku)
685127be1a7SAdrian Chadd 			return dp;
686127be1a7SAdrian Chadd 	}
687127be1a7SAdrian Chadd 	return NULL;
688127be1a7SAdrian Chadd }
689127be1a7SAdrian Chadd 
690127be1a7SAdrian Chadd /*
691127be1a7SAdrian Chadd  * Lookup a regdomain by name.
692127be1a7SAdrian Chadd  */
693127be1a7SAdrian Chadd const struct regdomain *
694127be1a7SAdrian Chadd lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
695127be1a7SAdrian Chadd {
696127be1a7SAdrian Chadd 	const struct regdomain *dp;
697127be1a7SAdrian Chadd 
698127be1a7SAdrian Chadd 	LIST_FOREACH(dp, &rdp->domains, next) {
699127be1a7SAdrian Chadd 		if (strcasecmp(dp->name, name) == 0)
700127be1a7SAdrian Chadd 			return dp;
701127be1a7SAdrian Chadd 	}
702127be1a7SAdrian Chadd 	return NULL;
703127be1a7SAdrian Chadd }
704127be1a7SAdrian Chadd 
705127be1a7SAdrian Chadd /*
706127be1a7SAdrian Chadd  * Lookup a country by ISO country code.
707127be1a7SAdrian Chadd  */
708127be1a7SAdrian Chadd const struct country *
709127be1a7SAdrian Chadd lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
710127be1a7SAdrian Chadd {
711127be1a7SAdrian Chadd 	const struct country *cp;
712127be1a7SAdrian Chadd 
713127be1a7SAdrian Chadd 	LIST_FOREACH(cp, &rdp->countries, next) {
714127be1a7SAdrian Chadd 		if (cp->code == cc)
715127be1a7SAdrian Chadd 			return cp;
716127be1a7SAdrian Chadd 	}
717127be1a7SAdrian Chadd 	return NULL;
718127be1a7SAdrian Chadd }
719127be1a7SAdrian Chadd 
720127be1a7SAdrian Chadd /*
721127be1a7SAdrian Chadd  * Lookup a country by ISO/long name.
722127be1a7SAdrian Chadd  */
723127be1a7SAdrian Chadd const struct country *
724127be1a7SAdrian Chadd lib80211_country_findbyname(const struct regdata *rdp, const char *name)
725127be1a7SAdrian Chadd {
726127be1a7SAdrian Chadd 	const struct country *cp;
727127be1a7SAdrian Chadd 	int len;
728127be1a7SAdrian Chadd 
729127be1a7SAdrian Chadd 	len = strlen(name);
730127be1a7SAdrian Chadd 	LIST_FOREACH(cp, &rdp->countries, next) {
731127be1a7SAdrian Chadd 		if (strcasecmp(cp->isoname, name) == 0)
732127be1a7SAdrian Chadd 			return cp;
733127be1a7SAdrian Chadd 	}
734127be1a7SAdrian Chadd 	LIST_FOREACH(cp, &rdp->countries, next) {
735127be1a7SAdrian Chadd 		if (strncasecmp(cp->name, name, len) == 0)
736127be1a7SAdrian Chadd 			return cp;
737127be1a7SAdrian Chadd 	}
738127be1a7SAdrian Chadd 	return NULL;
739127be1a7SAdrian Chadd }
740