drm/i915: Set GPU freq to idle_freq initially
[dragonfly.git] / sbin / ifconfig / regdomain.c
blobeaef5f1fa35d419861a62b474dd837eec47a6bfb
1 /*-
2 * Copyright (c) 2008 Sam Leffler, Errno Consulting
3 * All rights reserved.
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.
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.
25 #ifndef lint
26 static const char rcsid[] = "$FreeBSD: head/sbin/ifconfig/regdomain.c 200587 2009-12-15 20:44:12Z gavin $";
27 #endif /* not lint */
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/mman.h>
32 #include <sys/sbuf.h>
33 #include <sys/stat.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <err.h>
40 #include <unistd.h>
42 #include <bsdxml.h>
44 #include "regdomain.h"
46 #include <netproto/802_11/_ieee80211.h>
48 #define MAXLEVEL 20
50 struct mystate {
51 XML_Parser parser;
52 struct regdata *rdp;
53 struct regdomain *rd; /* current domain */
54 struct netband *netband; /* current netband */
55 struct freqband *freqband; /* current freqband */
56 struct country *country; /* current country */
57 netband_head *curband; /* current netband list */
58 int level;
59 struct sbuf *sbuf[MAXLEVEL];
60 int nident;
63 struct ident {
64 const void *id;
65 void *p;
66 enum { DOMAIN, COUNTRY, FREQBAND } type;
69 static void
70 start_element(void *data, const char *name, const char **attr)
72 #define iseq(a,b) (strcasecmp(a,b) == 0)
73 struct mystate *mt;
74 const void *id, *ref, *mode;
75 int i;
77 mt = data;
78 if (++mt->level == MAXLEVEL) {
79 /* XXX force parser to abort */
80 return;
82 mt->sbuf[mt->level] = sbuf_new_auto();
83 id = ref = mode = NULL;
84 for (i = 0; attr[i] != NULL; i += 2) {
85 if (iseq(attr[i], "id")) {
86 id = attr[i+1];
87 } else if (iseq(attr[i], "ref")) {
88 ref = attr[i+1];
89 } else if (iseq(attr[i], "mode")) {
90 mode = attr[i+1];
91 } else
92 printf("%*.*s[%s = %s]\n", mt->level + 1,
93 mt->level + 1, "", attr[i], attr[i+1]);
95 if (iseq(name, "rd") && mt->rd == NULL) {
96 if (mt->country == NULL) {
97 mt->rd = calloc(1, sizeof(struct regdomain));
98 mt->rd->name = strdup(id);
99 mt->nident++;
100 LIST_INSERT_HEAD(&mt->rdp->domains, mt->rd, next);
101 } else
102 mt->country->rd = (void *)strdup(ref);
103 return;
105 if (iseq(name, "defcc") && mt->rd != NULL) {
106 mt->rd->cc = (void *)strdup(ref);
107 return;
109 if (iseq(name, "netband") && mt->curband == NULL && mt->rd != NULL) {
110 if (mode == NULL) {
111 warnx("no mode for netband at line %ld",
112 XML_GetCurrentLineNumber(mt->parser));
113 return;
115 if (iseq(mode, "11b"))
116 mt->curband = &mt->rd->bands_11b;
117 else if (iseq(mode, "11g"))
118 mt->curband = &mt->rd->bands_11g;
119 else if (iseq(mode, "11a"))
120 mt->curband = &mt->rd->bands_11a;
121 else if (iseq(mode, "11ng"))
122 mt->curband = &mt->rd->bands_11ng;
123 else if (iseq(mode, "11na"))
124 mt->curband = &mt->rd->bands_11na;
125 else
126 warnx("unknown mode \"%s\" at line %ld",
127 __DECONST(char *, mode),
128 XML_GetCurrentLineNumber(mt->parser));
129 return;
131 if (iseq(name, "band") && mt->netband == NULL) {
132 if (mt->curband == NULL) {
133 warnx("band without enclosing netband at line %ld",
134 XML_GetCurrentLineNumber(mt->parser));
135 return;
137 mt->netband = calloc(1, sizeof(struct netband));
138 LIST_INSERT_HEAD(mt->curband, mt->netband, next);
139 return;
141 if (iseq(name, "freqband") && mt->freqband == NULL && mt->netband != NULL) {
142 /* XXX handle inlines and merge into table? */
143 if (mt->netband->band != NULL) {
144 warnx("duplicate freqband at line %ld ignored",
145 XML_GetCurrentLineNumber(mt->parser));
146 /* XXX complain */
147 } else
148 mt->netband->band = (void *)strdup(ref);
149 return;
152 if (iseq(name, "country") && mt->country == NULL) {
153 mt->country = calloc(1, sizeof(struct country));
154 mt->country->isoname = strdup(id);
155 mt->country->code = NO_COUNTRY;
156 mt->nident++;
157 LIST_INSERT_HEAD(&mt->rdp->countries, mt->country, next);
158 return;
161 if (iseq(name, "freqband") && mt->freqband == NULL) {
162 mt->freqband = calloc(1, sizeof(struct freqband));
163 mt->freqband->id = strdup(id);
164 mt->nident++;
165 LIST_INSERT_HEAD(&mt->rdp->freqbands, mt->freqband, next);
166 return;
168 #undef iseq
171 static int
172 decode_flag(struct mystate *mt, const char *p, int len)
174 #define iseq(a,b) (strcasecmp(a,b) == 0)
175 static const struct {
176 const char *name;
177 int len;
178 uint32_t value;
179 } flags[] = {
180 #define FLAG(x) { #x, sizeof(#x)-1, x }
181 FLAG(IEEE80211_CHAN_A),
182 FLAG(IEEE80211_CHAN_B),
183 FLAG(IEEE80211_CHAN_G),
184 FLAG(IEEE80211_CHAN_HT20),
185 FLAG(IEEE80211_CHAN_HT40),
186 FLAG(IEEE80211_CHAN_ST),
187 FLAG(IEEE80211_CHAN_TURBO),
188 FLAG(IEEE80211_CHAN_PASSIVE),
189 FLAG(IEEE80211_CHAN_DFS),
190 FLAG(IEEE80211_CHAN_CCK),
191 FLAG(IEEE80211_CHAN_OFDM),
192 FLAG(IEEE80211_CHAN_2GHZ),
193 FLAG(IEEE80211_CHAN_5GHZ),
194 FLAG(IEEE80211_CHAN_DYN),
195 FLAG(IEEE80211_CHAN_GFSK),
196 FLAG(IEEE80211_CHAN_GSM),
197 FLAG(IEEE80211_CHAN_STURBO),
198 FLAG(IEEE80211_CHAN_HALF),
199 FLAG(IEEE80211_CHAN_QUARTER),
200 FLAG(IEEE80211_CHAN_HT40U),
201 FLAG(IEEE80211_CHAN_HT40D),
202 FLAG(IEEE80211_CHAN_4MSXMIT),
203 FLAG(IEEE80211_CHAN_NOADHOC),
204 FLAG(IEEE80211_CHAN_NOHOSTAP),
205 FLAG(IEEE80211_CHAN_11D),
206 FLAG(IEEE80211_CHAN_FHSS),
207 FLAG(IEEE80211_CHAN_PUREG),
208 FLAG(IEEE80211_CHAN_108A),
209 FLAG(IEEE80211_CHAN_108G),
210 #undef FLAG
211 { "ECM", 3, REQ_ECM },
212 { "INDOOR", 6, REQ_INDOOR },
213 { "OUTDOOR", 7, REQ_OUTDOOR },
215 int i;
217 for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++)
218 if (len == flags[i].len && iseq(p, flags[i].name))
219 return flags[i].value;
220 warnx("unknown flag \"%.*s\" at line %ld ignored",
221 len, p, XML_GetCurrentLineNumber(mt->parser));
222 return 0;
223 #undef iseq
226 static void
227 end_element(void *data, const char *name)
229 #define iseq(a,b) (strcasecmp(a,b) == 0)
230 struct mystate *mt;
231 int len;
232 char *p;
234 mt = data;
235 sbuf_finish(mt->sbuf[mt->level]);
236 p = sbuf_data(mt->sbuf[mt->level]);
237 len = sbuf_len(mt->sbuf[mt->level]);
239 /* <freqband>...</freqband> */
240 if (iseq(name, "freqstart") && mt->freqband != NULL) {
241 mt->freqband->freqStart = strtoul(p, NULL, 0);
242 goto done;
244 if (iseq(name, "freqend") && mt->freqband != NULL) {
245 mt->freqband->freqEnd = strtoul(p, NULL, 0);
246 goto done;
248 if (iseq(name, "chanwidth") && mt->freqband != NULL) {
249 mt->freqband->chanWidth = strtoul(p, NULL, 0);
250 goto done;
252 if (iseq(name, "chansep") && mt->freqband != NULL) {
253 mt->freqband->chanSep = strtoul(p, NULL, 0);
254 goto done;
256 if (iseq(name, "flags")) {
257 if (mt->freqband != NULL)
258 mt->freqband->flags |= decode_flag(mt, p, len);
259 else if (mt->netband != NULL)
260 mt->netband->flags |= decode_flag(mt, p, len);
261 else {
262 warnx("flags without freqband or netband at line %ld ignored",
263 XML_GetCurrentLineNumber(mt->parser));
265 goto done;
268 /* <rd> ... </rd> */
269 if (iseq(name, "name") && mt->rd != NULL) {
270 mt->rd->name = strdup(p);
271 goto done;
273 if (iseq(name, "sku") && mt->rd != NULL) {
274 mt->rd->sku = strtoul(p, NULL, 0);
275 goto done;
277 if (iseq(name, "netband") && mt->rd != NULL) {
278 mt->curband = NULL;
279 goto done;
282 /* <band> ... </band> */
283 if (iseq(name, "freqband") && mt->netband != NULL) {
284 /* XXX handle inline freqbands */
285 goto done;
287 if (iseq(name, "maxpower") && mt->netband != NULL) {
288 mt->netband->maxPower = strtoul(p, NULL, 0);
289 goto done;
291 if (iseq(name, "maxpowerdfs") && mt->netband != NULL) {
292 mt->netband->maxPowerDFS = strtoul(p, NULL, 0);
293 goto done;
295 if (iseq(name, "maxantgain") && mt->netband != NULL) {
296 mt->netband->maxAntGain = strtoul(p, NULL, 0);
297 goto done;
300 /* <country>...</country> */
301 if (iseq(name, "isocc") && mt->country != NULL) {
302 mt->country->code = strtoul(p, NULL, 0);
303 goto done;
305 if (iseq(name, "name") && mt->country != NULL) {
306 mt->country->name = strdup(p);
307 goto done;
310 if (len != 0) {
311 warnx("unexpected XML token \"%s\" data \"%s\" at line %ld",
312 name, p, XML_GetCurrentLineNumber(mt->parser));
313 /* XXX goto done? */
315 /* </freqband> */
316 if (iseq(name, "freqband") && mt->freqband != NULL) {
317 /* XXX must have start/end frequencies */
318 /* XXX must have channel width/sep */
319 mt->freqband = NULL;
320 goto done;
322 /* </rd> */
323 if (iseq(name, "rd") && mt->rd != NULL) {
324 mt->rd = NULL;
325 goto done;
327 /* </band> */
328 if (iseq(name, "band") && mt->netband != NULL) {
329 if (mt->netband->band == NULL) {
330 warnx("no freqbands for band at line %ld",
331 XML_GetCurrentLineNumber(mt->parser));
333 if (mt->netband->maxPower == 0) {
334 warnx("no maxpower for band at line %ld",
335 XML_GetCurrentLineNumber(mt->parser));
337 /* default max power w/ DFS to max power */
338 if (mt->netband->maxPowerDFS == 0)
339 mt->netband->maxPowerDFS = mt->netband->maxPower;
340 mt->netband = NULL;
341 goto done;
343 /* </netband> */
344 if (iseq(name, "netband") && mt->netband != NULL) {
345 mt->curband = NULL;
346 goto done;
348 /* </country> */
349 if (iseq(name, "country") && mt->country != NULL) {
350 if (mt->country->code == NO_COUNTRY) {
351 warnx("no ISO cc for country at line %ld",
352 XML_GetCurrentLineNumber(mt->parser));
354 if (mt->country->name == NULL) {
355 warnx("no name for country at line %ld",
356 XML_GetCurrentLineNumber(mt->parser));
358 if (mt->country->rd == NULL) {
359 warnx("no regdomain reference for country at line %ld",
360 XML_GetCurrentLineNumber(mt->parser));
362 mt->country = NULL;
363 goto done;
365 done:
366 sbuf_delete(mt->sbuf[mt->level]);
367 mt->sbuf[mt->level--] = NULL;
368 #undef iseq
371 static void
372 char_data(void *data, const XML_Char *s, int len)
374 struct mystate *mt;
375 const char *b, *e;
377 mt = data;
379 b = s;
380 e = s + len-1;
381 for (; isspace(*b) && b < e; b++)
383 for (; isspace(*e) && e > b; e++)
385 if (e != b || (*b != '\0' && !isspace(*b)))
386 sbuf_bcat(mt->sbuf[mt->level], b, e-b+1);
389 static void *
390 findid(struct regdata *rdp, const void *id, int type)
392 struct ident *ip;
394 for (ip = rdp->ident; ip->id != NULL; ip++)
395 if (ip->type == type && strcasecmp(ip->id, id) == 0)
396 return ip->p;
397 return NULL;
401 * Parse an regdomain XML configuration and build the internal representation.
404 lib80211_regdomain_readconfig(struct regdata *rdp, const void *p, size_t len)
406 struct mystate *mt;
407 struct regdomain *dp;
408 struct country *cp;
409 struct freqband *fp;
410 struct netband *nb;
411 const void *id;
412 int i, errors;
414 memset(rdp, 0, sizeof(struct regdata));
415 mt = calloc(1, sizeof(struct mystate));
416 if (mt == NULL)
417 return ENOMEM;
418 /* parse the XML input */
419 mt->rdp = rdp;
420 mt->parser = XML_ParserCreate(NULL);
421 XML_SetUserData(mt->parser, mt);
422 XML_SetElementHandler(mt->parser, start_element, end_element);
423 XML_SetCharacterDataHandler(mt->parser, char_data);
424 if (XML_Parse(mt->parser, p, len, 1) != XML_STATUS_OK) {
425 warnx("%s: %s at line %ld", __func__,
426 XML_ErrorString(XML_GetErrorCode(mt->parser)),
427 XML_GetCurrentLineNumber(mt->parser));
428 return -1;
430 XML_ParserFree(mt->parser);
432 /* setup the identifer table */
433 rdp->ident = calloc(sizeof(struct ident), mt->nident + 1);
434 if (rdp->ident == NULL)
435 return ENOMEM;
436 free(mt);
438 errors = 0;
439 i = 0;
440 LIST_FOREACH(dp, &rdp->domains, next) {
441 rdp->ident[i].id = dp->name;
442 rdp->ident[i].p = dp;
443 rdp->ident[i].type = DOMAIN;
444 i++;
446 LIST_FOREACH(fp, &rdp->freqbands, next) {
447 rdp->ident[i].id = fp->id;
448 rdp->ident[i].p = fp;
449 rdp->ident[i].type = FREQBAND;
450 i++;
452 LIST_FOREACH(cp, &rdp->countries, next) {
453 rdp->ident[i].id = cp->isoname;
454 rdp->ident[i].p = cp;
455 rdp->ident[i].type = COUNTRY;
456 i++;
459 /* patch references */
460 LIST_FOREACH(dp, &rdp->domains, next) {
461 if (dp->cc != NULL) {
462 id = dp->cc;
463 dp->cc = findid(rdp, id, COUNTRY);
464 if (dp->cc == NULL) {
465 warnx("undefined country \"%s\"",
466 __DECONST(char *, id));
467 errors++;
469 free(__DECONST(char *, id));
471 LIST_FOREACH(nb, &dp->bands_11b, next) {
472 id = findid(rdp, nb->band, FREQBAND);
473 if (id == NULL) {
474 warnx("undefined 11b band \"%s\"",
475 __DECONST(char *, nb->band));
476 errors++;
478 nb->band = id;
480 LIST_FOREACH(nb, &dp->bands_11g, next) {
481 id = findid(rdp, nb->band, FREQBAND);
482 if (id == NULL) {
483 warnx("undefined 11g band \"%s\"",
484 __DECONST(char *, nb->band));
485 errors++;
487 nb->band = id;
489 LIST_FOREACH(nb, &dp->bands_11a, next) {
490 id = findid(rdp, nb->band, FREQBAND);
491 if (id == NULL) {
492 warnx("undefined 11a band \"%s\"",
493 __DECONST(char *, nb->band));
494 errors++;
496 nb->band = id;
498 LIST_FOREACH(nb, &dp->bands_11ng, next) {
499 id = findid(rdp, nb->band, FREQBAND);
500 if (id == NULL) {
501 warnx("undefined 11ng band \"%s\"",
502 __DECONST(char *, nb->band));
503 errors++;
505 nb->band = id;
507 LIST_FOREACH(nb, &dp->bands_11na, next) {
508 id = findid(rdp, nb->band, FREQBAND);
509 if (id == NULL) {
510 warnx("undefined 11na band \"%s\"",
511 __DECONST(char *, nb->band));
512 errors++;
514 nb->band = id;
517 LIST_FOREACH(cp, &rdp->countries, next) {
518 id = cp->rd;
519 cp->rd = findid(rdp, id, DOMAIN);
520 if (cp->rd == NULL) {
521 warnx("undefined country \"%s\"",
522 __DECONST(char *, id));
523 errors++;
525 free(__DECONST(char *, id));
528 return errors ? EINVAL : 0;
531 static void
532 cleanup_bands(netband_head *head)
534 struct netband *nb;
536 for (;;) {
537 nb = LIST_FIRST(head);
538 if (nb == NULL)
539 break;
540 free(nb);
545 * Cleanup state/resources for a previously parsed regdomain database.
547 void
548 lib80211_regdomain_cleanup(struct regdata *rdp)
551 free(rdp->ident);
552 rdp->ident = NULL;
553 for (;;) {
554 struct regdomain *dp = LIST_FIRST(&rdp->domains);
555 if (dp == NULL)
556 break;
557 LIST_REMOVE(dp, next);
558 cleanup_bands(&dp->bands_11b);
559 cleanup_bands(&dp->bands_11g);
560 cleanup_bands(&dp->bands_11a);
561 cleanup_bands(&dp->bands_11ng);
562 cleanup_bands(&dp->bands_11na);
563 if (dp->name != NULL)
564 free(__DECONST(char *, dp->name));
566 for (;;) {
567 struct country *cp = LIST_FIRST(&rdp->countries);
568 if (cp == NULL)
569 break;
570 LIST_REMOVE(cp, next);
571 if (cp->name != NULL)
572 free(__DECONST(char *, cp->name));
573 free(cp);
575 for (;;) {
576 struct freqband *fp = LIST_FIRST(&rdp->freqbands);
577 if (fp == NULL)
578 break;
579 LIST_REMOVE(fp, next);
580 free(fp);
584 struct regdata *
585 lib80211_alloc_regdata(void)
587 struct regdata *rdp;
588 struct stat sb;
589 void *xml;
590 int fd;
592 rdp = calloc(1, sizeof(struct regdata));
594 fd = open(_PATH_REGDOMAIN, O_RDONLY);
595 if (fd < 0) {
596 #ifdef DEBUG
597 warn("%s: open(%s)", __func__, _PATH_REGDOMAIN);
598 #endif
599 free(rdp);
600 return NULL;
602 if (fstat(fd, &sb) < 0) {
603 #ifdef DEBUG
604 warn("%s: fstat(%s)", __func__, _PATH_REGDOMAIN);
605 #endif
606 close(fd);
607 free(rdp);
608 return NULL;
610 xml = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
611 if (xml == MAP_FAILED) {
612 #ifdef DEBUG
613 warn("%s: mmap", __func__);
614 #endif
615 close(fd);
616 free(rdp);
617 return NULL;
619 if (lib80211_regdomain_readconfig(rdp, xml, sb.st_size) != 0) {
620 #ifdef DEBUG
621 warn("%s: error reading regulatory database", __func__);
622 #endif
623 munmap(xml, sb.st_size);
624 close(fd);
625 free(rdp);
626 return NULL;
628 munmap(xml, sb.st_size);
629 close(fd);
631 return rdp;
634 void
635 lib80211_free_regdata(struct regdata *rdp)
637 lib80211_regdomain_cleanup(rdp);
638 free(rdp);
642 * Lookup a regdomain by SKU.
644 const struct regdomain *
645 lib80211_regdomain_findbysku(const struct regdata *rdp, enum RegdomainCode sku)
647 const struct regdomain *dp;
649 LIST_FOREACH(dp, &rdp->domains, next) {
650 if (dp->sku == sku)
651 return dp;
653 return NULL;
657 * Lookup a regdomain by name.
659 const struct regdomain *
660 lib80211_regdomain_findbyname(const struct regdata *rdp, const char *name)
662 const struct regdomain *dp;
664 LIST_FOREACH(dp, &rdp->domains, next) {
665 if (strcasecmp(dp->name, name) == 0)
666 return dp;
668 return NULL;
672 * Lookup a country by ISO country code.
674 const struct country *
675 lib80211_country_findbycc(const struct regdata *rdp, enum ISOCountryCode cc)
677 const struct country *cp;
679 LIST_FOREACH(cp, &rdp->countries, next) {
680 if (cp->code == cc)
681 return cp;
683 return NULL;
687 * Lookup a country by ISO/long name.
689 const struct country *
690 lib80211_country_findbyname(const struct regdata *rdp, const char *name)
692 const struct country *cp;
693 int len;
695 len = strlen(name);
696 LIST_FOREACH(cp, &rdp->countries, next) {
697 if (strcasecmp(cp->isoname, name) == 0)
698 return cp;
700 LIST_FOREACH(cp, &rdp->countries, next) {
701 if (strncasecmp(cp->name, name, len) == 0)
702 return cp;
704 return NULL;