Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/lib80211/lib80211_regdomain.c
39475 views
1
/*-
2
* Copyright (c) 2008 Sam Leffler, Errno Consulting
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 ``AS IS'' AND ANY EXPRESS OR
15
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
*/
25
26
#include <sys/types.h>
27
#include <sys/errno.h>
28
#include <sys/param.h>
29
#include <sys/mman.h>
30
#include <sys/sbuf.h>
31
#include <sys/stat.h>
32
33
#include <stdio.h>
34
#include <string.h>
35
#include <ctype.h>
36
#include <fcntl.h>
37
#include <err.h>
38
#include <unistd.h>
39
40
#include <bsdxml.h>
41
42
#include "lib80211_regdomain.h"
43
44
#include <net80211/_ieee80211.h>
45
46
#define MAXLEVEL 20
47
48
struct mystate {
49
XML_Parser parser;
50
struct regdata *rdp;
51
struct regdomain *rd; /* current domain */
52
struct netband *netband; /* current netband */
53
struct freqband *freqband; /* current freqband */
54
struct country *country; /* current country */
55
netband_head *curband; /* current netband list */
56
int level;
57
struct sbuf *sbuf[MAXLEVEL];
58
int nident;
59
};
60
61
struct ident {
62
const void *id;
63
void *p;
64
enum { DOMAIN, COUNTRY, FREQBAND } type;
65
};
66
67
static void
68
start_element(void *data, const char *name, const char **attr)
69
{
70
#define iseq(a,b) (strcasecmp(a,b) == 0)
71
struct mystate *mt;
72
const void *id, *ref, *mode;
73
int i;
74
75
mt = data;
76
if (++mt->level == MAXLEVEL) {
77
/* XXX force parser to abort */
78
return;
79
}
80
mt->sbuf[mt->level] = sbuf_new_auto();
81
id = ref = mode = NULL;
82
for (i = 0; attr[i] != NULL; i += 2) {
83
if (iseq(attr[i], "id")) {
84
id = attr[i+1];
85
} else if (iseq(attr[i], "ref")) {
86
ref = attr[i+1];
87
} else if (iseq(attr[i], "mode")) {
88
mode = attr[i+1];
89
} else
90
printf("%*.*s[%s = %s]\n", mt->level + 1,
91
mt->level + 1, "", attr[i], attr[i+1]);
92
}
93
if (iseq(name, "rd") && mt->rd == NULL) {
94
if (mt->country == NULL) {
95
mt->rd = calloc(1, sizeof(struct regdomain));
96
mt->rd->name = strdup(id);
97
mt->nident++;
98
LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
99
} else
100
mt->country->rd = (void *)strdup(ref);
101
return;
102
}
103
if (iseq(name, "defcc") && mt->rd != NULL) {
104
mt->rd->cc = (void *)strdup(ref);
105
return;
106
}
107
if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
108
if (mode == NULL) {
109
warnx("no mode for netband at line %ld",
110
XML_GetCurrentLineNumber(mt->parser));
111
return;
112
}
113
if (iseq(mode, "11b"))
114
mt->curband = &mt->rd->bands_11b;
115
else if (iseq(mode, "11g"))
116
mt->curband = &mt->rd->bands_11g;
117
else if (iseq(mode, "11a"))
118
mt->curband = &mt->rd->bands_11a;
119
else if (iseq(mode, "11ng"))
120
mt->curband = &mt->rd->bands_11ng;
121
else if (iseq(mode, "11na"))
122
mt->curband = &mt->rd->bands_11na;
123
else if (iseq(mode, "11ac"))
124
mt->curband = &mt->rd->bands_11ac;
125
else if (iseq(mode, "11acg"))
126
mt->curband = &mt->rd->bands_11acg;
127
else
128
warnx("unknown mode \"%s\" at line %ld",
129
__DECONST(char *, mode),
130
XML_GetCurrentLineNumber(mt->parser));
131
return;
132
}
133
if (iseq(name, "band") && mt->netband == NULL) {
134
if (mt->curband == NULL) {
135
warnx("band without enclosing netband at line %ld",
136
XML_GetCurrentLineNumber(mt->parser));
137
return;
138
}
139
mt->netband = calloc(1, sizeof(struct netband));
140
LIST_INSERT_HEAD(mt->curband, mt->netband, next);
141
return;
142
}
143
if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
144
/* XXX handle inlines and merge into table? */
145
if (mt->netband->band != NULL) {
146
warnx("duplicate freqband at line %ld ignored",
147
XML_GetCurrentLineNumber(mt->parser));
148
/* XXX complain */
149
} else
150
mt->netband->band = (void *)strdup(ref);
151
return;
152
}
153
154
if (iseq(name, "country") && mt->country == NULL) {
155
mt->country = calloc(1, sizeof(struct country));
156
mt->country->isoname = strdup(id);
157
mt->country->code = NO_COUNTRY;
158
mt->nident++;
159
LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
160
return;
161
}
162
163
if (iseq(name, "freqband") && mt->freqband == NULL) {
164
mt->freqband = calloc(1, sizeof(struct freqband));
165
mt->freqband->id = strdup(id);
166
mt->nident++;
167
LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
168
return;
169
}
170
#undef iseq
171
}
172
173
static int
174
decode_flag(struct mystate *mt, const char *p, int len)
175
{
176
#define iseq(a,b) (strcasecmp(a,b) == 0)
177
static const struct {
178
const char *name;
179
int len;
180
uint32_t value;
181
} flags[] = {
182
#define FLAG(x) { #x, sizeof(#x)-1, x }
183
FLAG(IEEE80211_CHAN_A),
184
FLAG(IEEE80211_CHAN_B),
185
FLAG(IEEE80211_CHAN_G),
186
FLAG(IEEE80211_CHAN_HT20),
187
FLAG(IEEE80211_CHAN_HT40),
188
FLAG(IEEE80211_CHAN_VHT20),
189
FLAG(IEEE80211_CHAN_VHT40),
190
FLAG(IEEE80211_CHAN_VHT80),
191
FLAG(IEEE80211_CHAN_VHT160),
192
/*
193
* XXX VHT80P80? This likely should be done by
194
* 80MHz chan logic in net80211 / ifconfig.
195
*/
196
FLAG(IEEE80211_CHAN_ST),
197
FLAG(IEEE80211_CHAN_TURBO),
198
FLAG(IEEE80211_CHAN_PASSIVE),
199
FLAG(IEEE80211_CHAN_DFS),
200
FLAG(IEEE80211_CHAN_CCK),
201
FLAG(IEEE80211_CHAN_OFDM),
202
FLAG(IEEE80211_CHAN_2GHZ),
203
FLAG(IEEE80211_CHAN_5GHZ),
204
FLAG(IEEE80211_CHAN_DYN),
205
FLAG(IEEE80211_CHAN_GFSK),
206
FLAG(IEEE80211_CHAN_GSM),
207
FLAG(IEEE80211_CHAN_STURBO),
208
FLAG(IEEE80211_CHAN_HALF),
209
FLAG(IEEE80211_CHAN_QUARTER),
210
FLAG(IEEE80211_CHAN_HT40U),
211
FLAG(IEEE80211_CHAN_HT40D),
212
FLAG(IEEE80211_CHAN_4MSXMIT),
213
FLAG(IEEE80211_CHAN_NOADHOC),
214
FLAG(IEEE80211_CHAN_NOHOSTAP),
215
FLAG(IEEE80211_CHAN_11D),
216
FLAG(IEEE80211_CHAN_FHSS),
217
FLAG(IEEE80211_CHAN_PUREG),
218
FLAG(IEEE80211_CHAN_108A),
219
FLAG(IEEE80211_CHAN_108G),
220
#undef FLAG
221
{ "ECM", 3, REQ_ECM },
222
{ "INDOOR", 6, REQ_INDOOR },
223
{ "OUTDOOR", 7, REQ_OUTDOOR },
224
};
225
unsigned int i;
226
227
for (i = 0; i < nitems(flags); i++)
228
if (len == flags[i].len && iseq(p, flags[i].name))
229
return flags[i].value;
230
warnx("unknown flag \"%.*s\" at line %ld ignored",
231
len, p, XML_GetCurrentLineNumber(mt->parser));
232
return 0;
233
#undef iseq
234
}
235
236
static void
237
end_element(void *data, const char *name)
238
{
239
#define iseq(a,b) (strcasecmp(a,b) == 0)
240
struct mystate *mt;
241
int len;
242
char *p;
243
244
mt = data;
245
sbuf_finish(mt->sbuf[mt->level]);
246
p = sbuf_data(mt->sbuf[mt->level]);
247
len = sbuf_len(mt->sbuf[mt->level]);
248
249
/* <freqband>...</freqband> */
250
if (iseq(name, "freqstart") && mt->freqband != NULL) {
251
mt->freqband->freqStart = strtoul(p, NULL, 0);
252
goto done;
253
}
254
if (iseq(name, "freqend") && mt->freqband != NULL) {
255
mt->freqband->freqEnd = strtoul(p, NULL, 0);
256
goto done;
257
}
258
if (iseq(name, "chanwidth") && mt->freqband != NULL) {
259
mt->freqband->chanWidth = strtoul(p, NULL, 0);
260
goto done;
261
}
262
if (iseq(name, "chansep") && mt->freqband != NULL) {
263
mt->freqband->chanSep = strtoul(p, NULL, 0);
264
goto done;
265
}
266
if (iseq(name, "flags")) {
267
if (mt->freqband != NULL)
268
mt->freqband->flags |= decode_flag(mt, p, len);
269
else if (mt->netband != NULL)
270
mt->netband->flags |= decode_flag(mt, p, len);
271
else {
272
warnx("flags without freqband or netband at line %ld ignored",
273
XML_GetCurrentLineNumber(mt->parser));
274
}
275
goto done;
276
}
277
278
/* <rd> ... </rd> */
279
if (iseq(name, "name") && mt->rd != NULL) {
280
mt->rd->name = strdup(p);
281
goto done;
282
}
283
if (iseq(name, "sku") && mt->rd != NULL) {
284
mt->rd->sku = strtoul(p, NULL, 0);
285
goto done;
286
}
287
if (iseq(name, "netband") && mt->rd != NULL) {
288
mt->curband = NULL;
289
goto done;
290
}
291
292
/* <band> ... </band> */
293
if (iseq(name, "freqband") && mt->netband != NULL) {
294
/* XXX handle inline freqbands */
295
goto done;
296
}
297
if (iseq(name, "maxpower") && mt->netband != NULL) {
298
mt->netband->maxPower = strtoul(p, NULL, 0);
299
goto done;
300
}
301
if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
302
mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
303
goto done;
304
}
305
if (iseq(name, "maxantgain") && mt->netband != NULL) {
306
mt->netband->maxAntGain = strtoul(p, NULL, 0);
307
goto done;
308
}
309
310
/* <country>...</country> */
311
if (iseq(name, "isocc") && mt->country != NULL) {
312
mt->country->code = strtoul(p, NULL, 0);
313
goto done;
314
}
315
if (iseq(name, "name") && mt->country != NULL) {
316
mt->country->name = strdup(p);
317
goto done;
318
}
319
320
if (len != 0) {
321
warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
322
name, p, XML_GetCurrentLineNumber(mt->parser));
323
/* XXX goto done? */
324
}
325
/* </freqband> */
326
if (iseq(name, "freqband") && mt->freqband != NULL) {
327
/* XXX must have start/end frequencies */
328
/* XXX must have channel width/sep */
329
mt->freqband = NULL;
330
goto done;
331
}
332
/* </rd> */
333
if (iseq(name, "rd") && mt->rd != NULL) {
334
mt->rd = NULL;
335
goto done;
336
}
337
/* </band> */
338
if (iseq(name, "band") && mt->netband != NULL) {
339
if (mt->netband->band == NULL) {
340
warnx("no freqbands for band at line %ld",
341
XML_GetCurrentLineNumber(mt->parser));
342
}
343
if (mt->netband->maxPower == 0) {
344
warnx("no maxpower for band at line %ld",
345
XML_GetCurrentLineNumber(mt->parser));
346
}
347
/* default max power w/ DFS to max power */
348
if (mt->netband->maxPowerDFS == 0)
349
mt->netband->maxPowerDFS = mt->netband->maxPower;
350
mt->netband = NULL;
351
goto done;
352
}
353
/* </netband> */
354
if (iseq(name, "netband") && mt->netband != NULL) {
355
mt->curband = NULL;
356
goto done;
357
}
358
/* </country> */
359
if (iseq(name, "country") && mt->country != NULL) {
360
/* XXX NO_COUNTRY should be in the net80211 country enum */
361
if ((int) mt->country->code == NO_COUNTRY) {
362
warnx("no ISO cc for country at line %ld",
363
XML_GetCurrentLineNumber(mt->parser));
364
}
365
if (mt->country->name == NULL) {
366
warnx("no name for country at line %ld",
367
XML_GetCurrentLineNumber(mt->parser));
368
}
369
if (mt->country->rd == NULL) {
370
warnx("no regdomain reference for country at line %ld",
371
XML_GetCurrentLineNumber(mt->parser));
372
}
373
mt->country = NULL;
374
goto done;
375
}
376
done:
377
sbuf_delete(mt->sbuf[mt->level]);
378
mt->sbuf[mt->level--] = NULL;
379
#undef iseq
380
}
381
382
static void
383
char_data(void *data, const XML_Char *s, int len)
384
{
385
struct mystate *mt;
386
const char *b, *e;
387
388
mt = data;
389
390
b = s;
391
e = s + len-1;
392
for (; isspace(*b) && b < e; b++)
393
;
394
for (; isspace(*e) && e > b; e++)
395
;
396
if (e != b || (*b != '\0' && !isspace(*b)))
397
sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
398
}
399
400
static void *
401
findid(struct regdata *rdp, const void *id, int type)
402
{
403
struct ident *ip;
404
405
for (ip = rdp->ident; ip->id != NULL; ip++)
406
if ((int) ip->type == type && strcasecmp(ip->id, id) == 0)
407
return ip->p;
408
return NULL;
409
}
410
411
/*
412
* Parse an regdomain XML configuration and build the internal representation.
413
*/
414
int
415
lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
416
{
417
struct mystate *mt;
418
struct regdomain *dp;
419
struct country *cp;
420
struct freqband *fp;
421
struct netband *nb;
422
const void *id;
423
int i, errors;
424
425
memset(rdp, 0, sizeof(struct regdata));
426
mt = calloc(1, sizeof(struct mystate));
427
if (mt == NULL)
428
return ENOMEM;
429
/* parse the XML input */
430
mt->rdp = rdp;
431
mt->parser = XML_ParserCreate(NULL);
432
XML_SetUserData(mt->parser, mt);
433
XML_SetElementHandler(mt->parser, start_element, end_element);
434
XML_SetCharacterDataHandler(mt->parser, char_data);
435
if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
436
warnx("%s: %s at line %ld", __func__,
437
XML_ErrorString(XML_GetErrorCode(mt->parser)),
438
XML_GetCurrentLineNumber(mt->parser));
439
return -1;
440
}
441
XML_ParserFree(mt->parser);
442
443
/* setup the identifer table */
444
rdp->ident = calloc(mt->nident + 1, sizeof(struct ident));
445
if (rdp->ident == NULL)
446
return ENOMEM;
447
free(mt);
448
449
errors = 0;
450
i = 0;
451
LIST_FOREACH(dp, &rdp->domains, next) {
452
rdp->ident[i].id = dp->name;
453
rdp->ident[i].p = dp;
454
rdp->ident[i].type = DOMAIN;
455
i++;
456
}
457
LIST_FOREACH(fp, &rdp->freqbands, next) {
458
rdp->ident[i].id = fp->id;
459
rdp->ident[i].p = fp;
460
rdp->ident[i].type = FREQBAND;
461
i++;
462
}
463
LIST_FOREACH(cp, &rdp->countries, next) {
464
rdp->ident[i].id = cp->isoname;
465
rdp->ident[i].p = cp;
466
rdp->ident[i].type = COUNTRY;
467
i++;
468
}
469
470
/* patch references */
471
LIST_FOREACH(dp, &rdp->domains, next) {
472
if (dp->cc != NULL) {
473
id = dp->cc;
474
dp->cc = findid(rdp, id, COUNTRY);
475
if (dp->cc == NULL) {
476
warnx("undefined country \"%s\"",
477
__DECONST(char *, id));
478
errors++;
479
}
480
free(__DECONST(char *, id));
481
}
482
LIST_FOREACH(nb, &dp->bands_11b, next) {
483
id = findid(rdp, nb->band, FREQBAND);
484
if (id == NULL) {
485
warnx("undefined 11b band \"%s\"",
486
__DECONST(char *, nb->band));
487
errors++;
488
}
489
nb->band = id;
490
}
491
LIST_FOREACH(nb, &dp->bands_11g, next) {
492
id = findid(rdp, nb->band, FREQBAND);
493
if (id == NULL) {
494
warnx("undefined 11g band \"%s\"",
495
__DECONST(char *, nb->band));
496
errors++;
497
}
498
nb->band = id;
499
}
500
LIST_FOREACH(nb, &dp->bands_11a, next) {
501
id = findid(rdp, nb->band, FREQBAND);
502
if (id == NULL) {
503
warnx("undefined 11a band \"%s\"",
504
__DECONST(char *, nb->band));
505
errors++;
506
}
507
nb->band = id;
508
}
509
LIST_FOREACH(nb, &dp->bands_11ng, next) {
510
id = findid(rdp, nb->band, FREQBAND);
511
if (id == NULL) {
512
warnx("undefined 11ng band \"%s\"",
513
__DECONST(char *, nb->band));
514
errors++;
515
}
516
nb->band = id;
517
}
518
LIST_FOREACH(nb, &dp->bands_11na, next) {
519
id = findid(rdp, nb->band, FREQBAND);
520
if (id == NULL) {
521
warnx("undefined 11na band \"%s\"",
522
__DECONST(char *, nb->band));
523
errors++;
524
}
525
nb->band = id;
526
}
527
LIST_FOREACH(nb, &dp->bands_11ac, next) {
528
id = findid(rdp, nb->band, FREQBAND);
529
if (id == NULL) {
530
warnx("undefined 11ac band \"%s\"",
531
__DECONST(char *, nb->band));
532
errors++;
533
}
534
nb->band = id;
535
}
536
LIST_FOREACH(nb, &dp->bands_11acg, next) {
537
id = findid(rdp, nb->band, FREQBAND);
538
if (id == NULL) {
539
warnx("undefined 11acg band \"%s\"",
540
__DECONST(char *, nb->band));
541
errors++;
542
}
543
nb->band = id;
544
}
545
}
546
LIST_FOREACH(cp, &rdp->countries, next) {
547
id = cp->rd;
548
cp->rd = findid(rdp, id, DOMAIN);
549
if (cp->rd == NULL) {
550
warnx("undefined country \"%s\"",
551
__DECONST(char *, id));
552
errors++;
553
}
554
free(__DECONST(char *, id));
555
}
556
557
return errors ? EINVAL : 0;
558
}
559
560
static void
561
cleanup_bands(netband_head *head)
562
{
563
struct netband *nb;
564
565
for (;;) {
566
nb = LIST_FIRST(head);
567
if (nb == NULL)
568
break;
569
LIST_REMOVE(nb, next);
570
free(nb);
571
}
572
}
573
574
/*
575
* Cleanup state/resources for a previously parsed regdomain database.
576
*/
577
void
578
lib80211_regdomain_cleanup(struct regdata *rdp)
579
{
580
581
free(rdp->ident);
582
rdp->ident = NULL;
583
for (;;) {
584
struct regdomain *dp = LIST_FIRST(&rdp->domains);
585
if (dp == NULL)
586
break;
587
LIST_REMOVE(dp, next);
588
cleanup_bands(&dp->bands_11b);
589
cleanup_bands(&dp->bands_11g);
590
cleanup_bands(&dp->bands_11a);
591
cleanup_bands(&dp->bands_11ng);
592
cleanup_bands(&dp->bands_11na);
593
cleanup_bands(&dp->bands_11ac);
594
cleanup_bands(&dp->bands_11acg);
595
if (dp->name != NULL)
596
free(__DECONST(char *, dp->name));
597
}
598
for (;;) {
599
struct country *cp = LIST_FIRST(&rdp->countries);
600
if (cp == NULL)
601
break;
602
LIST_REMOVE(cp, next);
603
if (cp->name != NULL)
604
free(__DECONST(char *, cp->name));
605
free(cp);
606
}
607
for (;;) {
608
struct freqband *fp = LIST_FIRST(&rdp->freqbands);
609
if (fp == NULL)
610
break;
611
LIST_REMOVE(fp, next);
612
free(fp);
613
}
614
}
615
616
struct regdata *
617
lib80211_alloc_regdata(void)
618
{
619
struct regdata *rdp;
620
struct stat sb;
621
void *xml;
622
int fd;
623
624
rdp = calloc(1, sizeof(struct regdata));
625
626
fd = open(_PATH_REGDOMAIN, O_RDONLY);
627
if (fd < 0) {
628
#ifdef DEBUG
629
warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
630
#endif
631
free(rdp);
632
return NULL;
633
}
634
if (fstat(fd, &sb) < 0) {
635
#ifdef DEBUG
636
warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
637
#endif
638
close(fd);
639
free(rdp);
640
return NULL;
641
}
642
xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
643
if (xml == MAP_FAILED) {
644
#ifdef DEBUG
645
warn("%s: mmap", __func__);
646
#endif
647
close(fd);
648
free(rdp);
649
return NULL;
650
}
651
if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
652
#ifdef DEBUG
653
warn("%s: error reading regulatory database", __func__);
654
#endif
655
munmap(xml, sb.st_size);
656
close(fd);
657
free(rdp);
658
return NULL;
659
}
660
munmap(xml, sb.st_size);
661
close(fd);
662
663
return rdp;
664
}
665
666
void
667
lib80211_free_regdata(struct regdata *rdp)
668
{
669
lib80211_regdomain_cleanup(rdp);
670
free(rdp);
671
}
672
673
/*
674
* Lookup a regdomain by SKU.
675
*/
676
const struct regdomain *
677
lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
678
{
679
const struct regdomain *dp;
680
681
LIST_FOREACH(dp, &rdp->domains, next) {
682
if (dp->sku == sku)
683
return dp;
684
}
685
return NULL;
686
}
687
688
/*
689
* Lookup a regdomain by name.
690
*/
691
const struct regdomain *
692
lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
693
{
694
const struct regdomain *dp;
695
696
LIST_FOREACH(dp, &rdp->domains, next) {
697
if (strcasecmp(dp->name, name) == 0)
698
return dp;
699
}
700
return NULL;
701
}
702
703
/*
704
* Lookup a country by ISO country code.
705
*/
706
const struct country *
707
lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
708
{
709
const struct country *cp;
710
711
LIST_FOREACH(cp, &rdp->countries, next) {
712
if (cp->code == cc)
713
return cp;
714
}
715
return NULL;
716
}
717
718
/*
719
* Lookup a country by ISO/long name.
720
*/
721
const struct country *
722
lib80211_country_findbyname(const struct regdata *rdp, const char *name)
723
{
724
const struct country *cp;
725
int len;
726
727
len = strlen(name);
728
LIST_FOREACH(cp, &rdp->countries, next) {
729
if (strcasecmp(cp->isoname, name) == 0)
730
return cp;
731
}
732
LIST_FOREACH(cp, &rdp->countries, next) {
733
if (strncasecmp(cp->name, name, len) == 0)
734
return cp;
735
}
736
return NULL;
737
}
738
739