Index: head/lib/lib80211/lib80211_regdomain.c =================================================================== --- head/lib/lib80211/lib80211_regdomain.c (revision 311576) +++ head/lib/lib80211/lib80211_regdomain.c (revision 311577) @@ -1,707 +1,739 @@ /*- * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "lib80211_regdomain.h" #include #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 if (iseq(mode, "11ac")) + mt->curband = &mt->rd->bands_11ac; + else if (iseq(mode, "11acg")) + mt->curband = &mt->rd->bands_11acg; 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_VHT20), + FLAG(IEEE80211_CHAN_VHT40), + FLAG(IEEE80211_CHAN_VHT80), + /* + * XXX VHT80_80? This likely should be done by + * 80MHz chan logic in net80211 / ifconfig. + */ + FLAG(IEEE80211_CHAN_VHT160), 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]); /* ... */ 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; } /* ... */ 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; } /* ... */ 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; } /* ... */ 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? */ } /* */ if (iseq(name, "freqband") && mt->freqband != NULL) { /* XXX must have start/end frequencies */ /* XXX must have channel width/sep */ mt->freqband = NULL; goto done; } /* */ if (iseq(name, "rd") && mt->rd != NULL) { mt->rd = NULL; goto done; } /* */ 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; } /* */ if (iseq(name, "netband") && mt->netband != NULL) { mt->curband = NULL; goto done; } /* */ 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(nb, &dp->bands_11ac, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11ac band \"%s\"", + __DECONST(char *, nb->band)); + errors++; + } + nb->band = id; + } + LIST_FOREACH(nb, &dp->bands_11acg, next) { + id = findid(rdp, nb->band, FREQBAND); + if (id == NULL) { + warnx("undefined 11acg 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); + cleanup_bands(&dp->bands_11ac); + cleanup_bands(&dp->bands_11acg); 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; } Index: head/lib/lib80211/lib80211_regdomain.h =================================================================== --- head/lib/lib80211/lib80211_regdomain.h (revision 311576) +++ head/lib/lib80211/lib80211_regdomain.h (revision 311577) @@ -1,121 +1,123 @@ /*- * Copyright (c) 2007-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. * * $FreeBSD$ */ #ifndef _LIB80211_REGDOMAIN_H_ #define _LIB80211_REGDOMAIN_H_ #include #include #include __BEGIN_DECLS struct freqband { uint16_t freqStart; /* starting frequency (MHz) */ uint16_t freqEnd; /* ending frequency (MHz) */ uint8_t chanWidth; /* channel width (MHz) */ uint8_t chanSep; /* channel sepaaration (MHz) */ uint32_t flags; /* common operational constraints */ const void *id; LIST_ENTRY(freqband) next; }; /* private flags, don't pass to os */ #define REQ_ECM 0x1 /* enable if ECM set */ #define REQ_INDOOR 0x2 /* enable only for indoor operation */ #define REQ_OUTDOOR 0x4 /* enable only for outdoor operation */ #define REQ_FLAGS (REQ_ECM|REQ_INDOOR|REQ_OUTDOOR) struct netband { const struct freqband *band; /* channel list description */ uint8_t maxPower; /* regulatory cap on tx power (dBm) */ uint8_t maxPowerDFS; /* regulatory cap w/ DFS (dBm) */ uint8_t maxAntGain; /* max allowed antenna gain (.5 dBm) */ uint32_t flags; /* net80211 channel flags */ LIST_ENTRY(netband) next; }; typedef LIST_HEAD(, netband) netband_head; struct country; struct regdomain { enum RegdomainCode sku; /* regdomain code/SKU */ const char *name; /* printable name */ const struct country *cc; /* country code for 1-1/default map */ netband_head bands_11b; /* 11b operation */ netband_head bands_11g; /* 11g operation */ netband_head bands_11a; /* 11a operation */ netband_head bands_11ng;/* 11ng operation */ netband_head bands_11na;/* 11na operation */ + netband_head bands_11ac;/* 11ac 5GHz operation */ + netband_head bands_11acg;/* 11ac 2GHz operation */ LIST_ENTRY(regdomain) next; }; struct country { enum ISOCountryCode code; #define NO_COUNTRY 0xffff const struct regdomain *rd; const char* isoname; const char* name; LIST_ENTRY(country) next; }; struct ident; struct regdata { LIST_HEAD(, country) countries; /* country code table */ LIST_HEAD(, regdomain) domains; /* regulatory domains */ LIST_HEAD(, freqband) freqbands; /* frequency band table */ struct ident *ident; /* identifier table */ }; #define _PATH_REGDOMAIN "/etc/regdomain.xml" struct regdata *lib80211_alloc_regdata(void); void lib80211_free_regdata(struct regdata *); int lib80211_regdomain_readconfig(struct regdata *, const void *, size_t); void lib80211_regdomain_cleanup(struct regdata *); const struct regdomain *lib80211_regdomain_findbysku(const struct regdata *, enum RegdomainCode); const struct regdomain *lib80211_regdomain_findbyname(const struct regdata *, const char *); const struct country *lib80211_country_findbycc(const struct regdata *, enum ISOCountryCode); const struct country *lib80211_country_findbyname(const struct regdata *, const char *); __END_DECLS #endif /* _LIB80211_REGDOMAIN_H_ */