Changeset View
Changeset View
Standalone View
Standalone View
lib/lib80211/lib80211_regdomain.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2008 Sam Leffler, Errno Consulting | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#ifndef lint | |||||
static const char rcsid[] = "$FreeBSD$"; | |||||
#endif /* not lint */ | |||||
#include <sys/types.h> | |||||
#include <sys/errno.h> | |||||
#include <sys/param.h> | |||||
#include <sys/mman.h> | |||||
#include <sys/sbuf.h> | |||||
#include <sys/stat.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <ctype.h> | |||||
#include <fcntl.h> | |||||
#include <err.h> | |||||
#include <unistd.h> | |||||
#include <bsdxml.h> | |||||
#include "lib80211_regdomain.h" | |||||
#include <net80211/_ieee80211.h> | |||||
#define MAXLEVEL 20 | |||||
struct mystate { | |||||
XML_Parser parser; | |||||
struct regdata *rdp; | |||||
struct regdomain *rd; /* current domain */ | |||||
struct netband *netband; /* current netband */ | |||||
struct freqband *freqband; /* current freqband */ | |||||
struct country *country; /* current country */ | |||||
netband_head *curband; /* current netband list */ | |||||
int level; | |||||
struct sbuf *sbuf[MAXLEVEL]; | |||||
int nident; | |||||
}; | |||||
struct ident { | |||||
const void *id; | |||||
void *p; | |||||
enum { DOMAIN, COUNTRY, FREQBAND } type; | |||||
}; | |||||
static void | |||||
start_element(void *data, const char *name, const char **attr) | |||||
{ | |||||
#define iseq(a,b) (strcasecmp(a,b) == 0) | |||||
struct mystate *mt; | |||||
const void *id, *ref, *mode; | |||||
int i; | |||||
mt = data; | |||||
if (++mt->level == MAXLEVEL) { | |||||
/* XXX force parser to abort */ | |||||
return; | |||||
} | |||||
mt->sbuf[mt->level] = sbuf_new_auto(); | |||||
id = ref = mode = NULL; | |||||
for (i = 0; attr[i] != NULL; i += 2) { | |||||
if (iseq(attr[i], "id")) { | |||||
id = attr[i+1]; | |||||
} else if (iseq(attr[i], "ref")) { | |||||
ref = attr[i+1]; | |||||
} else if (iseq(attr[i], "mode")) { | |||||
mode = attr[i+1]; | |||||
} else | |||||
printf("%*.*s[%s = %s]\n", mt->level + 1, | |||||
mt->level + 1, "", attr[i], attr[i+1]); | |||||
} | |||||
if (iseq(name, "rd") && mt->rd == NULL) { | |||||
if (mt->country == NULL) { | |||||
mt->rd = calloc(1, sizeof(struct regdomain)); | |||||
mt->rd->name = strdup(id); | |||||
mt->nident++; | |||||
LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next); | |||||
} else | |||||
mt->country->rd = (void *)strdup(ref); | |||||
return; | |||||
} | |||||
if (iseq(name, "defcc") && mt->rd != NULL) { | |||||
mt->rd->cc = (void *)strdup(ref); | |||||
return; | |||||
} | |||||
if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) { | |||||
if (mode == NULL) { | |||||
warnx("no mode for netband at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
return; | |||||
} | |||||
if (iseq(mode, "11b")) | |||||
mt->curband = &mt->rd->bands_11b; | |||||
else if (iseq(mode, "11g")) | |||||
mt->curband = &mt->rd->bands_11g; | |||||
else if (iseq(mode, "11a")) | |||||
mt->curband = &mt->rd->bands_11a; | |||||
else if (iseq(mode, "11ng")) | |||||
mt->curband = &mt->rd->bands_11ng; | |||||
else if (iseq(mode, "11na")) | |||||
mt->curband = &mt->rd->bands_11na; | |||||
else | |||||
warnx("unknown mode \"%s\" at line %ld", | |||||
__DECONST(char *, mode), | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
return; | |||||
} | |||||
if (iseq(name, "band") && mt->netband == NULL) { | |||||
if (mt->curband == NULL) { | |||||
warnx("band without enclosing netband at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
return; | |||||
} | |||||
mt->netband = calloc(1, sizeof(struct netband)); | |||||
LIST_INSERT_HEAD(mt->curband, mt->netband, next); | |||||
return; | |||||
} | |||||
if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) { | |||||
/* XXX handle inlines and merge into table? */ | |||||
if (mt->netband->band != NULL) { | |||||
warnx("duplicate freqband at line %ld ignored", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
/* XXX complain */ | |||||
} else | |||||
mt->netband->band = (void *)strdup(ref); | |||||
return; | |||||
} | |||||
if (iseq(name, "country") && mt->country == NULL) { | |||||
mt->country = calloc(1, sizeof(struct country)); | |||||
mt->country->isoname = strdup(id); | |||||
mt->country->code = NO_COUNTRY; | |||||
mt->nident++; | |||||
LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next); | |||||
return; | |||||
} | |||||
if (iseq(name, "freqband") && mt->freqband == NULL) { | |||||
mt->freqband = calloc(1, sizeof(struct freqband)); | |||||
mt->freqband->id = strdup(id); | |||||
mt->nident++; | |||||
LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next); | |||||
return; | |||||
} | |||||
#undef iseq | |||||
} | |||||
static int | |||||
decode_flag(struct mystate *mt, const char *p, int len) | |||||
{ | |||||
#define iseq(a,b) (strcasecmp(a,b) == 0) | |||||
static const struct { | |||||
const char *name; | |||||
int len; | |||||
uint32_t value; | |||||
} flags[] = { | |||||
#define FLAG(x) { #x, sizeof(#x)-1, x } | |||||
FLAG(IEEE80211_CHAN_A), | |||||
FLAG(IEEE80211_CHAN_B), | |||||
FLAG(IEEE80211_CHAN_G), | |||||
FLAG(IEEE80211_CHAN_HT20), | |||||
FLAG(IEEE80211_CHAN_HT40), | |||||
FLAG(IEEE80211_CHAN_ST), | |||||
FLAG(IEEE80211_CHAN_TURBO), | |||||
FLAG(IEEE80211_CHAN_PASSIVE), | |||||
FLAG(IEEE80211_CHAN_DFS), | |||||
FLAG(IEEE80211_CHAN_CCK), | |||||
FLAG(IEEE80211_CHAN_OFDM), | |||||
FLAG(IEEE80211_CHAN_2GHZ), | |||||
FLAG(IEEE80211_CHAN_5GHZ), | |||||
FLAG(IEEE80211_CHAN_DYN), | |||||
FLAG(IEEE80211_CHAN_GFSK), | |||||
FLAG(IEEE80211_CHAN_GSM), | |||||
FLAG(IEEE80211_CHAN_STURBO), | |||||
FLAG(IEEE80211_CHAN_HALF), | |||||
FLAG(IEEE80211_CHAN_QUARTER), | |||||
FLAG(IEEE80211_CHAN_HT40U), | |||||
FLAG(IEEE80211_CHAN_HT40D), | |||||
FLAG(IEEE80211_CHAN_4MSXMIT), | |||||
FLAG(IEEE80211_CHAN_NOADHOC), | |||||
FLAG(IEEE80211_CHAN_NOHOSTAP), | |||||
FLAG(IEEE80211_CHAN_11D), | |||||
FLAG(IEEE80211_CHAN_FHSS), | |||||
FLAG(IEEE80211_CHAN_PUREG), | |||||
FLAG(IEEE80211_CHAN_108A), | |||||
FLAG(IEEE80211_CHAN_108G), | |||||
#undef FLAG | |||||
{ "ECM", 3, REQ_ECM }, | |||||
{ "INDOOR", 6, REQ_INDOOR }, | |||||
{ "OUTDOOR", 7, REQ_OUTDOOR }, | |||||
}; | |||||
unsigned int i; | |||||
for (i = 0; i < nitems(flags); i++) | |||||
if (len == flags[i].len && iseq(p, flags[i].name)) | |||||
return flags[i].value; | |||||
warnx("unknown flag \"%.*s\" at line %ld ignored", | |||||
len, p, XML_GetCurrentLineNumber(mt->parser)); | |||||
return 0; | |||||
#undef iseq | |||||
} | |||||
static void | |||||
end_element(void *data, const char *name) | |||||
{ | |||||
#define iseq(a,b) (strcasecmp(a,b) == 0) | |||||
struct mystate *mt; | |||||
int len; | |||||
char *p; | |||||
mt = data; | |||||
sbuf_finish(mt->sbuf[mt->level]); | |||||
p = sbuf_data(mt->sbuf[mt->level]); | |||||
len = sbuf_len(mt->sbuf[mt->level]); | |||||
/* <freqband>...</freqband> */ | |||||
if (iseq(name, "freqstart") && mt->freqband != NULL) { | |||||
mt->freqband->freqStart = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "freqend") && mt->freqband != NULL) { | |||||
mt->freqband->freqEnd = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "chanwidth") && mt->freqband != NULL) { | |||||
mt->freqband->chanWidth = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "chansep") && mt->freqband != NULL) { | |||||
mt->freqband->chanSep = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "flags")) { | |||||
if (mt->freqband != NULL) | |||||
mt->freqband->flags |= decode_flag(mt, p, len); | |||||
else if (mt->netband != NULL) | |||||
mt->netband->flags |= decode_flag(mt, p, len); | |||||
else { | |||||
warnx("flags without freqband or netband at line %ld ignored", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
goto done; | |||||
} | |||||
/* <rd> ... </rd> */ | |||||
if (iseq(name, "name") && mt->rd != NULL) { | |||||
mt->rd->name = strdup(p); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "sku") && mt->rd != NULL) { | |||||
mt->rd->sku = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "netband") && mt->rd != NULL) { | |||||
mt->curband = NULL; | |||||
goto done; | |||||
} | |||||
/* <band> ... </band> */ | |||||
if (iseq(name, "freqband") && mt->netband != NULL) { | |||||
/* XXX handle inline freqbands */ | |||||
goto done; | |||||
} | |||||
if (iseq(name, "maxpower") && mt->netband != NULL) { | |||||
mt->netband->maxPower = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "maxpowerdfs") && mt->netband != NULL) { | |||||
mt->netband->maxPowerDFS = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "maxantgain") && mt->netband != NULL) { | |||||
mt->netband->maxAntGain = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
/* <country>...</country> */ | |||||
if (iseq(name, "isocc") && mt->country != NULL) { | |||||
mt->country->code = strtoul(p, NULL, 0); | |||||
goto done; | |||||
} | |||||
if (iseq(name, "name") && mt->country != NULL) { | |||||
mt->country->name = strdup(p); | |||||
goto done; | |||||
} | |||||
if (len != 0) { | |||||
warnx("unexpected XML token \"%s\" data \"%s\" at line %ld", | |||||
name, p, XML_GetCurrentLineNumber(mt->parser)); | |||||
/* XXX goto done? */ | |||||
} | |||||
/* </freqband> */ | |||||
if (iseq(name, "freqband") && mt->freqband != NULL) { | |||||
/* XXX must have start/end frequencies */ | |||||
/* XXX must have channel width/sep */ | |||||
mt->freqband = NULL; | |||||
goto done; | |||||
} | |||||
/* </rd> */ | |||||
if (iseq(name, "rd") && mt->rd != NULL) { | |||||
mt->rd = NULL; | |||||
goto done; | |||||
} | |||||
/* </band> */ | |||||
if (iseq(name, "band") && mt->netband != NULL) { | |||||
if (mt->netband->band == NULL) { | |||||
warnx("no freqbands for band at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
if (mt->netband->maxPower == 0) { | |||||
warnx("no maxpower for band at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
/* default max power w/ DFS to max power */ | |||||
if (mt->netband->maxPowerDFS == 0) | |||||
mt->netband->maxPowerDFS = mt->netband->maxPower; | |||||
mt->netband = NULL; | |||||
goto done; | |||||
} | |||||
/* </netband> */ | |||||
if (iseq(name, "netband") && mt->netband != NULL) { | |||||
mt->curband = NULL; | |||||
goto done; | |||||
} | |||||
/* </country> */ | |||||
if (iseq(name, "country") && mt->country != NULL) { | |||||
/* XXX NO_COUNTRY should be in the net80211 country enum */ | |||||
if ((int) mt->country->code == NO_COUNTRY) { | |||||
warnx("no ISO cc for country at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
if (mt->country->name == NULL) { | |||||
warnx("no name for country at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
if (mt->country->rd == NULL) { | |||||
warnx("no regdomain reference for country at line %ld", | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
} | |||||
mt->country = NULL; | |||||
goto done; | |||||
} | |||||
done: | |||||
sbuf_delete(mt->sbuf[mt->level]); | |||||
mt->sbuf[mt->level--] = NULL; | |||||
#undef iseq | |||||
} | |||||
static void | |||||
char_data(void *data, const XML_Char *s, int len) | |||||
{ | |||||
struct mystate *mt; | |||||
const char *b, *e; | |||||
mt = data; | |||||
b = s; | |||||
e = s + len-1; | |||||
for (; isspace(*b) && b < e; b++) | |||||
; | |||||
for (; isspace(*e) && e > b; e++) | |||||
; | |||||
if (e != b || (*b != '\0' && !isspace(*b))) | |||||
sbuf_bcat(mt->sbuf[mt->level], b, e-b+1); | |||||
} | |||||
static void * | |||||
findid(struct regdata *rdp, const void *id, int type) | |||||
{ | |||||
struct ident *ip; | |||||
for (ip = rdp->ident; ip->id != NULL; ip++) | |||||
if ((int) ip->type == type && strcasecmp(ip->id, id) == 0) | |||||
return ip->p; | |||||
return NULL; | |||||
} | |||||
/* | |||||
* Parse an regdomain XML configuration and build the internal representation. | |||||
*/ | |||||
int | |||||
lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len) | |||||
{ | |||||
struct mystate *mt; | |||||
struct regdomain *dp; | |||||
struct country *cp; | |||||
struct freqband *fp; | |||||
struct netband *nb; | |||||
const void *id; | |||||
int i, errors; | |||||
memset(rdp, 0, sizeof(struct regdata)); | |||||
mt = calloc(1, sizeof(struct mystate)); | |||||
if (mt == NULL) | |||||
return ENOMEM; | |||||
/* parse the XML input */ | |||||
mt->rdp = rdp; | |||||
mt->parser = XML_ParserCreate(NULL); | |||||
XML_SetUserData(mt->parser, mt); | |||||
XML_SetElementHandler(mt->parser, start_element, end_element); | |||||
XML_SetCharacterDataHandler(mt->parser, char_data); | |||||
if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) { | |||||
warnx("%s: %s at line %ld", __func__, | |||||
XML_ErrorString(XML_GetErrorCode(mt->parser)), | |||||
XML_GetCurrentLineNumber(mt->parser)); | |||||
return -1; | |||||
} | |||||
XML_ParserFree(mt->parser); | |||||
/* setup the identifer table */ | |||||
rdp->ident = calloc(sizeof(struct ident), mt->nident + 1); | |||||
if (rdp->ident == NULL) | |||||
return ENOMEM; | |||||
free(mt); | |||||
errors = 0; | |||||
i = 0; | |||||
LIST_FOREACH(dp, &rdp->domains, next) { | |||||
rdp->ident[i].id = dp->name; | |||||
rdp->ident[i].p = dp; | |||||
rdp->ident[i].type = DOMAIN; | |||||
i++; | |||||
} | |||||
LIST_FOREACH(fp, &rdp->freqbands, next) { | |||||
rdp->ident[i].id = fp->id; | |||||
rdp->ident[i].p = fp; | |||||
rdp->ident[i].type = FREQBAND; | |||||
i++; | |||||
} | |||||
LIST_FOREACH(cp, &rdp->countries, next) { | |||||
rdp->ident[i].id = cp->isoname; | |||||
rdp->ident[i].p = cp; | |||||
rdp->ident[i].type = COUNTRY; | |||||
i++; | |||||
} | |||||
/* patch references */ | |||||
LIST_FOREACH(dp, &rdp->domains, next) { | |||||
if (dp->cc != NULL) { | |||||
id = dp->cc; | |||||
dp->cc = findid(rdp, id, COUNTRY); | |||||
if (dp->cc == NULL) { | |||||
warnx("undefined country \"%s\"", | |||||
__DECONST(char *, id)); | |||||
errors++; | |||||
} | |||||
free(__DECONST(char *, id)); | |||||
} | |||||
LIST_FOREACH(nb, &dp->bands_11b, next) { | |||||
id = findid(rdp, nb->band, FREQBAND); | |||||
if (id == NULL) { | |||||
warnx("undefined 11b band \"%s\"", | |||||
__DECONST(char *, nb->band)); | |||||
errors++; | |||||
} | |||||
nb->band = id; | |||||
} | |||||
LIST_FOREACH(nb, &dp->bands_11g, next) { | |||||
id = findid(rdp, nb->band, FREQBAND); | |||||
if (id == NULL) { | |||||
warnx("undefined 11g band \"%s\"", | |||||
__DECONST(char *, nb->band)); | |||||
errors++; | |||||
} | |||||
nb->band = id; | |||||
} | |||||
LIST_FOREACH(nb, &dp->bands_11a, next) { | |||||
id = findid(rdp, nb->band, FREQBAND); | |||||
if (id == NULL) { | |||||
warnx("undefined 11a band \"%s\"", | |||||
__DECONST(char *, nb->band)); | |||||
errors++; | |||||
} | |||||
nb->band = id; | |||||
} | |||||
LIST_FOREACH(nb, &dp->bands_11ng, next) { | |||||
id = findid(rdp, nb->band, FREQBAND); | |||||
if (id == NULL) { | |||||
warnx("undefined 11ng band \"%s\"", | |||||
__DECONST(char *, nb->band)); | |||||
errors++; | |||||
} | |||||
nb->band = id; | |||||
} | |||||
LIST_FOREACH(nb, &dp->bands_11na, next) { | |||||
id = findid(rdp, nb->band, FREQBAND); | |||||
if (id == NULL) { | |||||
warnx("undefined 11na band \"%s\"", | |||||
__DECONST(char *, nb->band)); | |||||
errors++; | |||||
} | |||||
nb->band = id; | |||||
} | |||||
} | |||||
LIST_FOREACH(cp, &rdp->countries, next) { | |||||
id = cp->rd; | |||||
cp->rd = findid(rdp, id, DOMAIN); | |||||
if (cp->rd == NULL) { | |||||
warnx("undefined country \"%s\"", | |||||
__DECONST(char *, id)); | |||||
errors++; | |||||
} | |||||
free(__DECONST(char *, id)); | |||||
} | |||||
return errors ? EINVAL : 0; | |||||
} | |||||
static void | |||||
cleanup_bands(netband_head *head) | |||||
{ | |||||
struct netband *nb; | |||||
for (;;) { | |||||
nb = LIST_FIRST(head); | |||||
if (nb == NULL) | |||||
break; | |||||
free(nb); | |||||
} | |||||
} | |||||
/* | |||||
* Cleanup state/resources for a previously parsed regdomain database. | |||||
*/ | |||||
void | |||||
lib80211_regdomain_cleanup(struct regdata *rdp) | |||||
{ | |||||
free(rdp->ident); | |||||
rdp->ident = NULL; | |||||
for (;;) { | |||||
struct regdomain *dp = LIST_FIRST(&rdp->domains); | |||||
if (dp == NULL) | |||||
break; | |||||
LIST_REMOVE(dp, next); | |||||
cleanup_bands(&dp->bands_11b); | |||||
cleanup_bands(&dp->bands_11g); | |||||
cleanup_bands(&dp->bands_11a); | |||||
cleanup_bands(&dp->bands_11ng); | |||||
cleanup_bands(&dp->bands_11na); | |||||
if (dp->name != NULL) | |||||
free(__DECONST(char *, dp->name)); | |||||
} | |||||
for (;;) { | |||||
struct country *cp = LIST_FIRST(&rdp->countries); | |||||
if (cp == NULL) | |||||
break; | |||||
LIST_REMOVE(cp, next); | |||||
if (cp->name != NULL) | |||||
free(__DECONST(char *, cp->name)); | |||||
free(cp); | |||||
} | |||||
for (;;) { | |||||
struct freqband *fp = LIST_FIRST(&rdp->freqbands); | |||||
if (fp == NULL) | |||||
break; | |||||
LIST_REMOVE(fp, next); | |||||
free(fp); | |||||
} | |||||
} | |||||
struct regdata * | |||||
lib80211_alloc_regdata(void) | |||||
{ | |||||
struct regdata *rdp; | |||||
struct stat sb; | |||||
void *xml; | |||||
int fd; | |||||
rdp = calloc(1, sizeof(struct regdata)); | |||||
fd = open(_PATH_REGDOMAIN, O_RDONLY); | |||||
if (fd < 0) { | |||||
#ifdef DEBUG | |||||
warn("%s: open(%s)", __func__, _PATH_REGDOMAIN); | |||||
#endif | |||||
free(rdp); | |||||
return NULL; | |||||
} | |||||
if (fstat(fd, &sb) < 0) { | |||||
#ifdef DEBUG | |||||
warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN); | |||||
#endif | |||||
close(fd); | |||||
free(rdp); | |||||
return NULL; | |||||
} | |||||
xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | |||||
if (xml == MAP_FAILED) { | |||||
#ifdef DEBUG | |||||
warn("%s: mmap", __func__); | |||||
#endif | |||||
close(fd); | |||||
free(rdp); | |||||
return NULL; | |||||
} | |||||
if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) { | |||||
#ifdef DEBUG | |||||
warn("%s: error reading regulatory database", __func__); | |||||
#endif | |||||
munmap(xml, sb.st_size); | |||||
close(fd); | |||||
free(rdp); | |||||
return NULL; | |||||
} | |||||
munmap(xml, sb.st_size); | |||||
close(fd); | |||||
return rdp; | |||||
} | |||||
void | |||||
lib80211_free_regdata(struct regdata *rdp) | |||||
{ | |||||
lib80211_regdomain_cleanup(rdp); | |||||
free(rdp); | |||||
} | |||||
/* | |||||
* Lookup a regdomain by SKU. | |||||
*/ | |||||
const struct regdomain * | |||||
lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku) | |||||
{ | |||||
const struct regdomain *dp; | |||||
LIST_FOREACH(dp, &rdp->domains, next) { | |||||
if (dp->sku == sku) | |||||
return dp; | |||||
} | |||||
return NULL; | |||||
} | |||||
/* | |||||
* Lookup a regdomain by name. | |||||
*/ | |||||
const struct regdomain * | |||||
lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name) | |||||
{ | |||||
const struct regdomain *dp; | |||||
LIST_FOREACH(dp, &rdp->domains, next) { | |||||
if (strcasecmp(dp->name, name) == 0) | |||||
return dp; | |||||
} | |||||
return NULL; | |||||
} | |||||
/* | |||||
* Lookup a country by ISO country code. | |||||
*/ | |||||
const struct country * | |||||
lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc) | |||||
{ | |||||
const struct country *cp; | |||||
LIST_FOREACH(cp, &rdp->countries, next) { | |||||
if (cp->code == cc) | |||||
return cp; | |||||
} | |||||
return NULL; | |||||
} | |||||
/* | |||||
* Lookup a country by ISO/long name. | |||||
*/ | |||||
const struct country * | |||||
lib80211_country_findbyname(const struct regdata *rdp, const char *name) | |||||
{ | |||||
const struct country *cp; | |||||
int len; | |||||
len = strlen(name); | |||||
LIST_FOREACH(cp, &rdp->countries, next) { | |||||
if (strcasecmp(cp->isoname, name) == 0) | |||||
return cp; | |||||
} | |||||
LIST_FOREACH(cp, &rdp->countries, next) { | |||||
if (strncasecmp(cp->name, name, len) == 0) | |||||
return cp; | |||||
} | |||||
return NULL; | |||||
} |