DHDAXCW-Rockchip-OpenWrt/package/firmware/wireless-regdb/patches/700-regdb-fix-compatibility-with-python.patch
2020-02-21 21:25:39 +08:00

616 lines
19 KiB
Diff

diff --git a/db2bin.py b/db2bin.py
--- a/db2bin.py
+++ b/db2bin.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-from cStringIO import StringIO
+from io import BytesIO, open
import struct
import hashlib
from dbparse import DBParser
@@ -10,21 +10,21 @@
VERSION = 19
if len(sys.argv) < 3:
- print 'Usage: %s output-file input-file [key-file]' % sys.argv[0]
+ print('Usage: %s output-file input-file [key-file]' % sys.argv[0])
sys.exit(2)
def create_rules(countries):
result = {}
- for c in countries.itervalues():
+ for c in countries.values():
for rule in c.permissions:
result[rule] = 1
- return result.keys()
+ return list(result)
def create_collections(countries):
result = {}
- for c in countries.itervalues():
+ for c in countries.values():
result[c.permissions] = 1
- return result.keys()
+ return list(result)
def be32(output, val):
@@ -49,21 +49,25 @@
return self._offset
p = DBParser()
-countries = p.parse(file(sys.argv[2]))
+countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8'))
+
+countrynames = list(countries)
+countrynames.sort()
+
power = []
bands = []
-for c in countries.itervalues():
- for perm in c.permissions:
+for alpha2 in countrynames:
+ for perm in countries[alpha2].permissions:
if not perm.freqband in bands:
bands.append(perm.freqband)
if not perm.power in power:
power.append(perm.power)
rules = create_rules(countries)
-rules.sort(cmp=lambda x, y: cmp(x.freqband, y.freqband))
+rules.sort()
collections = create_collections(countries)
-collections.sort(cmp=lambda x, y: cmp(x[0].freqband, y[0].freqband))
+collections.sort()
-output = StringIO()
+output = BytesIO()
# struct regdb_file_header
be32(output, MAGIC)
@@ -104,19 +108,17 @@
# struct regdb_file_reg_rules_collection
coll = list(coll)
be32(output, len(coll))
- coll.sort(cmp=lambda x, y: cmp(x.freqband, y.freqband))
+ coll.sort()
for regrule in coll:
be32(output, reg_rules[regrule])
# update country pointer now!
reg_country_ptr.set()
-countrynames = countries.keys()
-countrynames.sort()
for alpha2 in countrynames:
coll = countries[alpha2]
# struct regdb_file_reg_country
- output.write(struct.pack('>ccxBI', str(alpha2[0]), str(alpha2[1]), coll.dfs_region, reg_rules_collections[coll.permissions]))
+ output.write(struct.pack('>2sxBI', alpha2, coll.dfs_region, reg_rules_collections[coll.permissions]))
if len(sys.argv) > 3:
@@ -141,5 +143,5 @@
else:
siglen.set(0)
-outfile = open(sys.argv[1], 'w')
+outfile = open(sys.argv[1], 'wb')
outfile.write(output.getvalue())
diff --git a/dbparse.py b/dbparse.py
--- a/dbparse.py
+++ b/dbparse.py
@@ -1,6 +1,9 @@
#!/usr/bin/env python
+from functools import total_ordering
import sys, math
+from math import ceil, log
+from collections import defaultdict, OrderedDict
# must match <linux/nl80211.h> enum nl80211_reg_rule_flags
@@ -25,6 +28,40 @@
'DFS-JP': 3,
}
+@total_ordering
+
+class WmmRule(object):
+
+ def __init__(self, vo_c, vi_c, be_c, bk_c, vo_ap, vi_ap, be_ap, bk_ap):
+ self.vo_c = vo_c
+ self.vi_c = vi_c
+ self.be_c = be_c
+ self.bk_c = bk_c
+ self.vo_ap = vo_ap
+ self.vi_ap = vi_ap
+ self.be_ap = be_ap
+ self.bk_ap = bk_ap
+
+ def _as_tuple(self):
+ return (self.vo_c, self.vi_c, self.be_c, self.bk_c,
+ self.vo_ap, self.vi_ap, self.be_ap, self.bk_ap)
+
+ def __eq__(self, other):
+ if other is None:
+ return False
+ return (self._as_tuple() == other._as_tuple())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ if other is None:
+ return False
+ return (self._as_tuple() < other._as_tuple())
+
+ def __hash__(self):
+ return hash(self._as_tuple())
+
class FreqBand(object):
def __init__(self, start, end, bw, comments=None):
self.start = start
@@ -32,41 +69,49 @@
self.maxbw = bw
self.comments = comments or []
- def __cmp__(self, other):
- s = self
- o = other
- if not isinstance(o, FreqBand):
- return False
- return cmp((s.start, s.end, s.maxbw), (o.start, o.end, o.maxbw))
+ def _as_tuple(self):
+ return (self.start, self.end, self.maxbw)
+
+ def __eq__(self, other):
+ return (self._as_tuple() == other._as_tuple())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return (self._as_tuple() < other._as_tuple())
def __hash__(self):
- s = self
- return hash((s.start, s.end, s.maxbw))
+ return hash(self._as_tuple())
def __str__(self):
return '<FreqBand %.3f - %.3f @ %.3f>' % (
self.start, self.end, self.maxbw)
+@total_ordering
class PowerRestriction(object):
def __init__(self, max_ant_gain, max_eirp, comments = None):
self.max_ant_gain = max_ant_gain
self.max_eirp = max_eirp
self.comments = comments or []
- def __cmp__(self, other):
- s = self
- o = other
- if not isinstance(o, PowerRestriction):
- return False
- return cmp((s.max_ant_gain, s.max_eirp),
- (o.max_ant_gain, o.max_eirp))
+ def _as_tuple(self):
+ return (self.max_ant_gain, self.max_eirp)
- def __str__(self):
- return '<PowerRestriction ...>'
+ def __eq__(self, other):
+ return (self._as_tuple() == other._as_tuple())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return (self._as_tuple() < other._as_tuple())
def __hash__(self):
- s = self
- return hash((s.max_ant_gain, s.max_eirp))
+ return hash(self._as_tuple())
+
+ def __str__(self):
+ return '<PowerRestriction ...>'
class DFSRegionError(Exception):
def __init__(self, dfs_region):
@@ -76,12 +121,15 @@
def __init__(self, flag):
self.flag = flag
+@total_ordering
class Permission(object):
- def __init__(self, freqband, power, flags):
+ def __init__(self, freqband, power, flags, wmmrule):
assert isinstance(freqband, FreqBand)
assert isinstance(power, PowerRestriction)
+ assert isinstance(wmmrule, WmmRule) or wmmrule is None
self.freqband = freqband
self.power = power
+ self.wmmrule = wmmrule
self.flags = 0
for flag in flags:
if not flag in flag_definitions:
@@ -90,26 +138,33 @@
self.textflags = flags
def _as_tuple(self):
- return (self.freqband, self.power, self.flags)
+ return (self.freqband, self.power, self.flags, self.wmmrule)
- def __cmp__(self, other):
- if not isinstance(other, Permission):
- return False
- return cmp(self._as_tuple(), other._as_tuple())
+ def __eq__(self, other):
+ return (self._as_tuple() == other._as_tuple())
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __lt__(self, other):
+ return (self._as_tuple() < other._as_tuple())
def __hash__(self):
return hash(self._as_tuple())
+ def __str__(self):
+ return str(self.freqband) + str(self.power) + str(self.wmmrule)
+
class Country(object):
def __init__(self, dfs_region, permissions=None, comments=None):
self._permissions = permissions or []
self.comments = comments or []
- self.dfs_region = 0
+ self.dfs_region = 0
- if dfs_region:
- if not dfs_region in dfs_regions:
- raise DFSRegionError(dfs_region)
- self.dfs_region = dfs_regions[dfs_region]
+ if dfs_region:
+ if not dfs_region in dfs_regions:
+ raise DFSRegionError(dfs_region)
+ self.dfs_region = dfs_regions[dfs_region]
def add(self, perm):
assert isinstance(perm, Permission)
@@ -233,6 +288,61 @@
self._powerrev[p] = pname
self._powerline[pname] = self._lineno
+ def _parse_wmmrule(self, line):
+ regions = line[:-1].strip()
+ if not regions:
+ self._syntax_error("'wmmrule' keyword must be followed by region")
+
+ regions = regions.split(',')
+
+ self._current_regions = {}
+ for region in regions:
+ if region in self._wmm_rules:
+ self._warn("region %s was added already to wmm rules" % region)
+ self._current_regions[region] = 1
+ self._comments = []
+
+ def _validate_input(self, cw_min, cw_max, aifsn, cot):
+ if cw_min < 1:
+ self._syntax_error("Invalid cw_min value (%d)" % cw_min)
+ if cw_max < 1:
+ self._syntax_error("Invalid cw_max value (%d)" % cw_max)
+ if cw_min > cw_max:
+ self._syntax_error("Inverted contention window (%d - %d)" %
+ (cw_min, cw_max))
+ if not (bin(cw_min + 1).count('1') == 1 and cw_min < 2**15):
+ self._syntax_error("Invalid cw_min value should be power of 2 - 1 (%d)"
+ % cw_min)
+ if not (bin(cw_max + 1).count('1') == 1 and cw_max < 2**15):
+ self._syntax_error("Invalid cw_max value should be power of 2 - 1 (%d)"
+ % cw_max)
+ if aifsn < 1:
+ self._syntax_error("Invalid aifsn value (%d)" % aifsn)
+ if cot < 0:
+ self._syntax_error("Invalid cot value (%d)" % cot)
+
+
+ def _validate_size(self, var, bytcnt):
+ return bytcnt < ceil(len(bin(var)[2:]) / 8.0)
+
+ def _parse_wmmrule_item(self, line):
+ bytcnt = (2.0, 2.0, 1.0, 2.0)
+ try:
+ ac, cval = line.split(':')
+ if not ac:
+ self._syntax_error("wmm item must have ac prefix")
+ except ValueError:
+ self._syntax_error("access category must be followed by colon")
+ p = tuple([int(v.split('=', 1)[1]) for v in cval.split(',')])
+ self._validate_input(*p)
+ for v, b in zip(p, bytcnt):
+ if self._validate_size(v, b):
+ self._syntax_error("unexpected input size expect %d got %d"
+ % (b, v))
+
+ for r in self._current_regions:
+ self._wmm_rules[r][ac] = p
+
def _parse_country(self, line):
try:
cname, cvals= line.split(':', 1)
@@ -248,6 +358,7 @@
for cname in cnames:
if len(cname) != 2:
self._warn("country '%s' not alpha2" % cname)
+ cname = cname.encode('ascii')
if not cname in self._countries:
self._countries[cname] = Country(dfs_region, comments=self._comments)
self._current_countries[cname] = self._countries[cname]
@@ -290,6 +401,15 @@
line = line.split(',')
pname = line[0]
flags = line[1:]
+ w = None
+ if flags and 'wmmrule' in flags[-1]:
+ try:
+ region = flags.pop().split('=', 1)[1]
+ if region not in self._wmm_rules.keys():
+ self._syntax_error("No wmm rule for %s" % region)
+ except IndexError:
+ self._syntax_error("flags is empty list or no region was found")
+ w = WmmRule(*self._wmm_rules[region].values())
if not bname in self._bands:
self._syntax_error("band does not exist")
@@ -303,10 +423,10 @@
b = self._bands[bname]
p = self._power[pname]
try:
- perm = Permission(b, p, flags)
- except FlagError, e:
+ perm = Permission(b, p, flags, w)
+ except FlagError as e:
self._syntax_error("Invalid flag '%s'" % e.flag)
- for cname, c in self._current_countries.iteritems():
+ for cname, c in self._current_countries.items():
if perm in c:
self._warn('Rule "%s, %s" added to "%s" twice' % (
bname, pname, cname))
@@ -315,6 +435,7 @@
def parse(self, f):
self._current_countries = None
+ self._current_regions = None
self._bands = {}
self._power = {}
self._countries = {}
@@ -326,6 +447,7 @@
self._powerdup = {}
self._bandline = {}
self._powerline = {}
+ self._wmm_rules = defaultdict(lambda: OrderedDict())
self._comments = []
@@ -337,6 +459,7 @@
self._comments.append(line[1:].strip())
line = line.replace(' ', '').replace('\t', '')
if not line:
+ self._current_regions = None
self._comments = []
line = line.split('#')[0]
if not line:
@@ -344,23 +467,35 @@
if line[0:4] == 'band':
self._parse_band(line[4:])
self._current_countries = None
+ self._current_regions = None
self._comments = []
elif line[0:5] == 'power':
self._parse_power(line[5:])
self._current_countries = None
+ self._current_regions = None
self._comments = []
elif line[0:7] == 'country':
self._parse_country(line[7:])
self._comments = []
+ self._current_regions = None
elif self._current_countries is not None:
+ self._current_regions = None
self._parse_country_item(line)
self._comments = []
+ elif line[0:7] == 'wmmrule':
+ self._parse_wmmrule(line[7:])
+ self._current_countries = None
+ self._comments = []
+ elif self._current_regions is not None:
+ self._parse_wmmrule_item(line)
+ self._current_countries = None
+ self._comments = []
else:
self._syntax_error("Expected band, power or country definition")
countries = self._countries
bands = {}
- for k, v in self._bands.iteritems():
+ for k, v in self._bands.items():
if k in self._bands_used:
bands[self._banddup[k]] = v
continue
@@ -369,7 +504,7 @@
self._lineno = self._bandline[k]
self._warn('Unused band definition "%s"' % k)
power = {}
- for k, v in self._power.iteritems():
+ for k, v in self._power.items():
if k in self._power_used:
power[self._powerdup[k]] = v
continue
diff --git /dev/null b/db2fw.py
--- /dev/null
+++ b/db2fw.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+
+from io import BytesIO, open
+import struct
+import hashlib
+from dbparse import DBParser
+import sys
+from math import log
+
+MAGIC = 0x52474442
+VERSION = 20
+
+if len(sys.argv) < 3:
+ print('Usage: %s output-file input-file' % sys.argv[0])
+ sys.exit(2)
+
+def create_rules(countries):
+ result = {}
+ for c in countries.values():
+ for rule in c.permissions:
+ result[rule] = 1
+ return list(result)
+
+def create_collections(countries):
+ result = {}
+ for c in countries.values():
+ result[(c.permissions, c.dfs_region)] = 1
+ return list(result)
+
+def create_wmms(countries):
+ result = {}
+ for c in countries.values():
+ for rule in c.permissions:
+ if rule.wmmrule is not None:
+ result[rule.wmmrule] = 1
+ return list(result)
+
+def be32(output, val):
+ output.write(struct.pack('>I', val))
+def be16(output, val):
+ output.write(struct.pack('>H', val))
+
+class PTR(object):
+ def __init__(self, output):
+ self._output = output
+ self._pos = output.tell()
+ be16(output, 0)
+ self._written = False
+
+ def set(self, val=None):
+ if val is None:
+ val = self._output.tell()
+ assert val & 3 == 0
+ self._offset = val
+ pos = self._output.tell()
+ self._output.seek(self._pos)
+ be16(self._output, val >> 2)
+ self._output.seek(pos)
+ self._written = True
+
+ def get(self):
+ return self._offset
+
+ @property
+ def written(self):
+ return self._written
+
+p = DBParser()
+countries = p.parse(open(sys.argv[2], 'r', encoding='utf-8'))
+rules = create_rules(countries)
+rules.sort()
+collections = create_collections(countries)
+collections.sort()
+wmms = create_wmms(countries)
+wmms.sort()
+
+output = BytesIO()
+
+# struct regdb_file_header
+be32(output, MAGIC)
+be32(output, VERSION)
+
+country_ptrs = {}
+countrynames = list(countries)
+countrynames.sort()
+for alpha2 in countrynames:
+ coll = countries[alpha2]
+ output.write(struct.pack('>2s', alpha2))
+ country_ptrs[alpha2] = PTR(output)
+output.write(b'\x00' * 4)
+
+wmmdb = {}
+for w in wmms:
+ assert output.tell() & 3 == 0
+ wmmdb[w] = output.tell() >> 2
+ for r in w._as_tuple():
+ ecw = int(log(r[0] + 1, 2)) << 4 | int(log(r[1] + 1, 2))
+ ac = (ecw, r[2],r[3])
+ output.write(struct.pack('>BBH', *ac))
+
+reg_rules = {}
+flags = 0
+for reg_rule in rules:
+ freq_range, power_rule, wmm_rule = reg_rule.freqband, reg_rule.power, reg_rule.wmmrule
+ reg_rules[reg_rule] = output.tell()
+ assert power_rule.max_ant_gain == 0
+ flags = 0
+ # convert to new rule flags
+ assert reg_rule.flags & ~0x899 == 0
+ if reg_rule.flags & 1<<0:
+ flags |= 1<<0
+ if reg_rule.flags & 1<<3:
+ flags |= 1<<1
+ if reg_rule.flags & 1<<4:
+ flags |= 1<<2
+ if reg_rule.flags & 1<<7:
+ flags |= 1<<3
+ if reg_rule.flags & 1<<11:
+ flags |= 1<<4
+ rule_len = 16
+ cac_timeout = 0 # TODO
+ if not (flags & 1<<2):
+ cac_timeout = 0
+ if cac_timeout or wmm_rule:
+ rule_len += 2
+ if wmm_rule is not None:
+ rule_len += 2
+ output.write(struct.pack('>BBHIII', rule_len, flags, int(power_rule.max_eirp * 100),
+ int(freq_range.start * 1000), int(freq_range.end * 1000), int(freq_range.maxbw * 1000),
+ ))
+ if rule_len > 16:
+ output.write(struct.pack('>H', cac_timeout))
+
+ if rule_len > 18:
+ be16(output, wmmdb[wmm_rule])
+
+ while rule_len % 4:
+ output.write('\0')
+ rule_len += 1
+
+for coll in collections:
+ for alpha2 in countrynames:
+ if (countries[alpha2].permissions, countries[alpha2].dfs_region) == coll:
+ assert not country_ptrs[alpha2].written
+ country_ptrs[alpha2].set()
+ slen = 3
+ output.write(struct.pack('>BBBx', slen, len(list(coll[0])), coll[1]))
+ coll = list(coll[0])
+ for regrule in coll:
+ be16(output, reg_rules[regrule] >> 2)
+ if len(coll) % 2:
+ be16(output, 0)
+
+for alpha2 in countrynames:
+ assert country_ptrs[alpha2].written
+
+outfile = open(sys.argv[1], 'wb')
+outfile.write(output.getvalue())