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