5 # must match <linux/nl80211.h> enum nl80211_reg_rule_flags
17 # hole at bit 9. FIXME: Where is NO-HT40 defined?
21 class FreqBand(object):
22 def __init__(self, start, end, bw, comments=None):
26 self.comments = comments or []
28 def __cmp__(self, other):
31 if not isinstance(o, FreqBand):
33 return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw))
37 return hash((s.start, s.end, s.maxbw))
40 return '<FreqBand %.3f - %.3f @ %.3f>' % (
41 self.start, self.end, self.maxbw)
43 class PowerRestriction(object):
44 def __init__(self, max_ant_gain, max_eirp, comments = None):
45 self.max_ant_gain = max_ant_gain
46 self.max_eirp = max_eirp
47 self.comments = comments or []
49 def __cmp__(self, other):
52 if not isinstance(o, PowerRestriction):
54 return cmp((s.max_ant_gain, s.max_eirp),
55 (o.max_ant_gain, o.max_eirp))
58 return '<PowerRestriction ...>'
62 return hash((s.max_ant_gain, s.max_eirp))
64 class FlagError(Exception):
65 def __init__(self, flag):
68 class Permission(object):
69 def __init__(self, freqband, power, flags):
70 assert isinstance(freqband, FreqBand)
71 assert isinstance(power, PowerRestriction)
72 self.freqband = freqband
76 if not flag in flag_definitions:
78 self.flags |= flag_definitions[flag]
79 self.textflags = flags
82 return (self.freqband, self.power, self.flags)
84 def __cmp__(self, other):
85 if not isinstance(other, Permission):
87 return cmp(self._as_tuple(), other._as_tuple())
90 return hash(self._as_tuple())
92 class Country(object):
93 def __init__(self, permissions=None, comments=None):
94 self._permissions = permissions or []
95 self.comments = comments or []
98 assert isinstance(perm, Permission)
99 self._permissions.append(perm)
100 self._permissions.sort()
102 def __contains__(self, perm):
103 assert isinstance(perm, Permission)
104 return perm in self._permissions
107 r = ['(%s, %s)' % (str(b), str(p)) for b, p in self._permissions]
108 return '<Country (%s)>' % (', '.join(r))
110 def _get_permissions_tuple(self):
111 return tuple(self._permissions)
112 permissions = property(_get_permissions_tuple)
114 class SyntaxError(Exception):
117 class DBParser(object):
118 def __init__(self, warn=None):
119 self._warn_callout = warn or sys.stderr.write
121 def _syntax_error(self, txt=None):
122 txt = txt and ' (%s)' % txt or ''
123 raise SyntaxError("Syntax error in line %d%s" % (self._lineno, txt))
125 def _warn(self, txt):
126 self._warn_callout("Warning (line %d): %s\n" % (self._lineno, txt))
128 def _parse_band_def(self, bname, banddef, dupwarn=True):
130 freqs, bw = banddef.split('@')
136 start, end = freqs.split('-')
140 self._syntax_error("band must have frequency range")
142 b = FreqBand(start, end, bw, comments=self._comments)
144 self._banddup[bname] = bname
145 if b in self._bandrev:
147 self._warn('Duplicate band definition ("%s" and "%s")' % (
148 bname, self._bandrev[b]))
149 self._banddup[bname] = self._bandrev[b]
150 self._bands[bname] = b
151 self._bandrev[b] = bname
152 self._bandline[bname] = self._lineno
154 def _parse_band(self, line):
156 bname, line = line.split(':', 1)
158 self._syntax_error("'band' keyword must be followed by name")
160 self._syntax_error("band name must be followed by colon")
162 if bname in flag_definitions:
163 self._syntax_error("Invalid band name")
165 self._parse_band_def(bname, line)
167 def _parse_power(self, line):
169 pname, line = line.split(':', 1)
171 self._syntax_error("'power' keyword must be followed by name")
173 self._syntax_error("power name must be followed by colon")
175 if pname in flag_definitions:
176 self._syntax_error("Invalid power name")
178 self._parse_power_def(pname, line)
180 def _parse_power_def(self, pname, line, dupwarn=True):
183 max_eirp) = line.split(',')
184 if max_ant_gain == 'N/A':
186 if max_eirp == 'N/A':
188 max_ant_gain = float(max_ant_gain)
190 if pwr.endswith('mW'):
191 pwr = float(pwr[:-2])
192 return 10.0 * math.log10(pwr)
195 max_eirp = conv_pwr(max_eirp)
197 self._syntax_error("invalid power data")
199 p = PowerRestriction(max_ant_gain, max_eirp,
200 comments=self._comments)
202 self._powerdup[pname] = pname
203 if p in self._powerrev:
205 self._warn('Duplicate power definition ("%s" and "%s")' % (
206 pname, self._powerrev[p]))
207 self._powerdup[pname] = self._powerrev[p]
208 self._power[pname] = p
209 self._powerrev[p] = pname
210 self._powerline[pname] = self._lineno
212 def _parse_country(self, line):
214 cname, line = line.split(':', 1)
216 self._syntax_error("'country' keyword must be followed by name")
218 self._syntax_error("extra data at end of country line")
220 self._syntax_error("country name must be followed by colon")
222 cnames = cname.split(',')
224 self._current_countries = {}
227 self._warn("country '%s' not alpha2" % cname)
228 if not cname in self._countries:
229 self._countries[cname] = Country(comments=self._comments)
230 self._current_countries[cname] = self._countries[cname]
233 def _parse_country_item(self, line):
236 band, line = line[1:].split('),', 1)
237 bname = 'UNNAMED %d' % self._lineno
238 self._parse_band_def(bname, band, dupwarn=False)
240 self._syntax_error("Badly parenthesised band definition")
243 bname, line = line.split(',', 1)
245 self._syntax_error("country definition must have band")
247 self._syntax_error("country definition must have power")
249 self._syntax_error("country definition must have band and power")
252 items = line.split('),', 1)
256 if not pname[-1] == ')':
257 self._syntax_error("Badly parenthesised power definition")
262 flags = items[1].split(',')
264 pname = 'UNNAMED %d' % self._lineno
265 self._parse_power_def(pname, power, dupwarn=False)
267 line = line.split(',')
271 if not bname in self._bands:
272 self._syntax_error("band does not exist")
273 if not pname in self._power:
274 self._syntax_error("power does not exist")
275 self._bands_used[bname] = True
276 self._power_used[pname] = True
277 # de-duplicate so binary database is more compact
278 bname = self._banddup[bname]
279 pname = self._powerdup[pname]
280 b = self._bands[bname]
281 p = self._power[pname]
283 perm = Permission(b, p, flags)
285 self._syntax_error("Invalid flag '%s'" % e.flag)
286 for cname, c in self._current_countries.iteritems():
288 self._warn('Rule "%s, %s" added to "%s" twice' % (
289 bname, pname, cname))
294 self._current_countries = None
298 self._bands_used = {}
299 self._power_used = {}
314 self._comments.append(line[1:].strip())
315 line = line.replace(' ', '').replace('\t', '')
318 line = line.split('#')[0]
321 if line[0:4] == 'band':
322 self._parse_band(line[4:])
323 self._current_countries = None
325 elif line[0:5] == 'power':
326 self._parse_power(line[5:])
327 self._current_countries = None
329 elif line[0:7] == 'country':
330 self._parse_country(line[7:])
332 elif self._current_countries is not None:
333 self._parse_country_item(line)
336 self._syntax_error("Expected band, power or country definition")
338 countries = self._countries
340 for k, v in self._bands.iteritems():
341 if k in self._bands_used:
342 bands[self._banddup[k]] = v
344 # we de-duplicated, but don't warn again about the dupes
345 if self._banddup[k] == k:
346 self._lineno = self._bandline[k]
347 self._warn('Unused band definition "%s"' % k)
349 for k, v in self._power.iteritems():
350 if k in self._power_used:
351 power[self._powerdup[k]] = v
353 # we de-duplicated, but don't warn again about the dupes
354 if self._powerdup[k] == k:
355 self._lineno = self._powerline[k]
356 self._warn('Unused power definition "%s"' % k)