mirror of
https://github.com/qca/qca-swiss-army-knife.git
synced 2025-12-10 07:44:42 +01:00
862 lines
25 KiB
Python
Executable file
862 lines
25 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
#
|
|
# Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
|
|
# Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
|
#
|
|
# Permission to use, copy, modify, and/or distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
#
|
|
# Run 'ath12k-bdencoder --help' to see the instructions
|
|
#
|
|
|
|
import struct
|
|
import ctypes
|
|
import os.path
|
|
import argparse
|
|
import json
|
|
import binascii
|
|
import hashlib
|
|
import tempfile
|
|
import subprocess
|
|
import logging
|
|
import sys
|
|
import shutil
|
|
|
|
MAX_BUF_LEN = 3000000
|
|
|
|
# the signature length also includes null byte and padding
|
|
ATH12K_BOARD_SIGNATURE = b"QCA-ATH12K-BOARD"
|
|
ATH12K_BOARD_SIGNATURE_LEN = 20
|
|
|
|
PADDING_MAGIC = 0x6d
|
|
DEFAULT_BD_API = 2
|
|
DEFAULT_BOARD_FILE = 'board-%d.bin' % DEFAULT_BD_API
|
|
DEFAULT_JSON_FILE = 'board-%d.json' % DEFAULT_BD_API
|
|
TYPE_LENGTH_SIZE = 8
|
|
|
|
ATH12K_BD_IE_BOARD = 0
|
|
ATH12K_BD_IE_REGDB = 1
|
|
|
|
ATH12K_BD_IE_BOARD_NAME = 0
|
|
ATH12K_BD_IE_BOARD_DATA = 1
|
|
|
|
ATH12K_BD_IE_REGDB_NAME = 0
|
|
ATH12K_BD_IE_REGDB_DATA = 1
|
|
|
|
|
|
def padding_needed(len):
|
|
if len % 4 != 0:
|
|
return 4 - len % 4
|
|
return 0
|
|
|
|
|
|
def add_ie(buf, offset, id, value):
|
|
length = len(value)
|
|
padding_len = padding_needed(length)
|
|
length = length + padding_len
|
|
|
|
padding = ctypes.create_string_buffer(padding_len)
|
|
|
|
for i in range(padding_len):
|
|
struct.pack_into('<B', padding, i, PADDING_MAGIC)
|
|
|
|
fmt = '<2i%ds%ds' % (len(value), padding_len)
|
|
struct.pack_into(fmt, buf, offset, id, len(value), value, padding.raw)
|
|
offset = offset + TYPE_LENGTH_SIZE + len(value) + padding_len
|
|
|
|
return offset
|
|
|
|
|
|
# to workaround annoying python feature of returning negative hex values
|
|
def hex32(val):
|
|
return val & 0xffffffff
|
|
|
|
|
|
# match with kernel crc32_le(0, buf, buf_len) implementation
|
|
def _crc32(buf):
|
|
return hex32(~(hex32(binascii.crc32(buf, 0xffffffff))))
|
|
|
|
|
|
def pretty_array_str(array):
|
|
return '\',\''.join(array)
|
|
|
|
|
|
class RegdbName():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
self = RegdbName()
|
|
fmt = '<%ds' % length
|
|
(name, ) = struct.unpack_from(fmt, buf, offset)
|
|
self.name = name.decode()
|
|
|
|
logging.debug('RegdbName.parse_ie(): offset %d length %d self %s' %
|
|
(offset, length, self))
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
return add_ie(buf, offset, ATH12K_BD_IE_REGDB_NAME, self.name.encode())
|
|
|
|
def __eq__(self, other):
|
|
return self.name == other.name
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
return 'RegdbName(%s)' % self.name
|
|
|
|
def __init__(self, name=None):
|
|
self.name = name
|
|
|
|
|
|
class RegdbData():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
self = RegdbData()
|
|
fmt = '<%ds' % length
|
|
(self.data, ) = struct.unpack_from(fmt, buf, offset)
|
|
|
|
logging.debug('RegdbData.parse_ie(): offset %d length %d self %s' %
|
|
(offset, length, self))
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
return add_ie(buf, offset, ATH12K_BD_IE_REGDB_DATA, self.data)
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
if self.data is not None:
|
|
s = '%d B' % (len(self.data))
|
|
else:
|
|
s = 'n/a'
|
|
|
|
return 'RegdbData(%s)' % (s)
|
|
|
|
def __init__(self, data=None):
|
|
self.data = data
|
|
|
|
|
|
class Regdb():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
logging.debug('Regdb.parse_ie(): offset %d length %d' % (offset, length))
|
|
|
|
self = Regdb()
|
|
|
|
# looping regdb IEs
|
|
while length > 0:
|
|
(ie_id, ie_len) = struct.unpack_from('<2i', buf, offset)
|
|
|
|
logging.debug('Regdb.parse_ie(): found ie_id %d ie_len %d offset %d length %d' %
|
|
(ie_id, ie_len, offset, length))
|
|
|
|
if TYPE_LENGTH_SIZE + ie_len > length:
|
|
raise Exception('Error: ie_len too big (%d > %d)' % (ie_len,
|
|
length))
|
|
|
|
offset += TYPE_LENGTH_SIZE
|
|
length -= TYPE_LENGTH_SIZE
|
|
|
|
if ie_id == ATH12K_BD_IE_REGDB_NAME:
|
|
self.names.append(RegdbName.parse_ie(buf, offset, ie_len))
|
|
elif ie_id == ATH12K_BD_IE_REGDB_DATA:
|
|
self.data = RegdbData.parse_ie(buf, offset, ie_len)
|
|
|
|
offset += ie_len + padding_needed(ie_len)
|
|
length -= ie_len + padding_needed(ie_len)
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
# store position ie header of this element
|
|
ie_offset = offset
|
|
|
|
offset += TYPE_LENGTH_SIZE
|
|
|
|
for name in self.names:
|
|
offset = name.add_to_buf(buf, offset)
|
|
|
|
offset = self.data.add_to_buf(buf, offset)
|
|
|
|
# write ie header as now we know the full length
|
|
ie_len = offset - ie_offset - TYPE_LENGTH_SIZE
|
|
struct.pack_into('<2i', buf, ie_offset, ATH12K_BD_IE_REGDB, ie_len)
|
|
|
|
return offset
|
|
|
|
def get_names_as_str(self):
|
|
names = []
|
|
for regdbname in self.names:
|
|
names.append(regdbname.name)
|
|
|
|
return names
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
names = []
|
|
for regdbname in self.names:
|
|
names.append(str(regdbname))
|
|
|
|
return 'Regdb(%s, %s)' % (','.join(names), self.data)
|
|
|
|
def __init__(self):
|
|
self.data = None
|
|
self.names = []
|
|
|
|
|
|
class BoardName():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
self = BoardName()
|
|
fmt = '<%ds' % length
|
|
(name, ) = struct.unpack_from(fmt, buf, offset)
|
|
self.name = name.decode()
|
|
|
|
logging.debug('BoardName.parse_ie(): offset %d length %d self %s' %
|
|
(offset, length, self))
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
return add_ie(buf, offset, ATH12K_BD_IE_BOARD_NAME, self.name.encode())
|
|
|
|
def __eq__(self, other):
|
|
return self.name == other.name
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
return 'BoardName(%s)' % self.name
|
|
|
|
def __init__(self, name=None):
|
|
self.name = name
|
|
|
|
|
|
class BoardData():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
self = BoardData()
|
|
fmt = '<%ds' % length
|
|
(self.data, ) = struct.unpack_from(fmt, buf, offset)
|
|
|
|
logging.debug('BoardData.parse_ie(): offset %d length %d self %s' %
|
|
(offset, length, self))
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
return add_ie(buf, offset, ATH12K_BD_IE_BOARD_DATA, self.data)
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
if self.data is not None:
|
|
s = '%d B' % (len(self.data))
|
|
else:
|
|
s = 'n/a'
|
|
|
|
return 'BoardData(%s)' % (s)
|
|
|
|
def __init__(self, data=None):
|
|
self.data = data
|
|
|
|
|
|
class Board():
|
|
@staticmethod
|
|
def parse_ie(buf, offset, length):
|
|
logging.debug('Board.parse_ie(): offset %d length %d' % (offset, length))
|
|
|
|
self = Board()
|
|
|
|
# looping board IEs
|
|
while length > 0:
|
|
(ie_id, ie_len) = struct.unpack_from('<2i', buf, offset)
|
|
|
|
logging.debug('Board.parse_ie(): found ie_id %d ie_len %d offset %d length %d' %
|
|
(ie_id, ie_len, offset, length))
|
|
|
|
if TYPE_LENGTH_SIZE + ie_len > length:
|
|
raise Exception('Error: ie_len too big (%d > %d)' % (ie_len,
|
|
length))
|
|
|
|
offset += TYPE_LENGTH_SIZE
|
|
length -= TYPE_LENGTH_SIZE
|
|
|
|
if ie_id == ATH12K_BD_IE_BOARD_NAME:
|
|
self.names.append(BoardName.parse_ie(buf, offset, ie_len))
|
|
elif ie_id == ATH12K_BD_IE_BOARD_DATA:
|
|
self.data = BoardData.parse_ie(buf, offset, ie_len)
|
|
|
|
offset += ie_len + padding_needed(ie_len)
|
|
length -= ie_len + padding_needed(ie_len)
|
|
|
|
return self
|
|
|
|
def add_to_buf(self, buf, offset):
|
|
# store position ie header of this element
|
|
ie_offset = offset
|
|
|
|
offset += TYPE_LENGTH_SIZE
|
|
|
|
for name in self.names:
|
|
offset = name.add_to_buf(buf, offset)
|
|
|
|
offset = self.data.add_to_buf(buf, offset)
|
|
|
|
# write ie header as now we know the full length
|
|
ie_len = offset - ie_offset - TYPE_LENGTH_SIZE
|
|
struct.pack_into('<2i', buf, ie_offset, ATH12K_BD_IE_BOARD, ie_len)
|
|
|
|
return offset
|
|
|
|
def get_names_as_str(self):
|
|
names = []
|
|
for boardname in self.names:
|
|
names.append(boardname.name)
|
|
|
|
return names
|
|
|
|
def __repr__(self):
|
|
return self.__str__()
|
|
|
|
def __str__(self):
|
|
names = []
|
|
for boardname in self.names:
|
|
names.append(str(boardname))
|
|
|
|
return 'Board(%s, %s)' % (','.join(names), self.data)
|
|
|
|
def __init__(self):
|
|
self.data = None
|
|
self.names = []
|
|
|
|
|
|
class BoardContainer:
|
|
|
|
def add_board(self, data, names):
|
|
boardnames = []
|
|
for name in names:
|
|
boardnames.append(BoardName(name))
|
|
|
|
board = Board()
|
|
board.data = BoardData(data)
|
|
board.names = boardnames
|
|
|
|
self.boards.append(board)
|
|
|
|
def add_regdb(self, data, names):
|
|
regdbnames = []
|
|
for name in names:
|
|
regdbnames.append(RegdbName(name))
|
|
|
|
regdb = Regdb()
|
|
regdb.data = RegdbData(data)
|
|
regdb.names = regdbnames
|
|
|
|
self.regdbs.append(regdb)
|
|
|
|
@staticmethod
|
|
def open_json(filename):
|
|
self = BoardContainer()
|
|
|
|
if not os.path.exists(filename):
|
|
print('mapping file %s not found' % (filename))
|
|
return
|
|
|
|
f = open(filename, 'r')
|
|
mapping = json.loads(f.read())
|
|
f.close()
|
|
|
|
if 'board' in mapping[0]:
|
|
for b in mapping[0]['board']:
|
|
board_filename = b['data']
|
|
f = open(board_filename, 'rb')
|
|
data = f.read()
|
|
f.close()
|
|
|
|
self.add_board(data, b['names'])
|
|
|
|
if 'regdb' in mapping[0]:
|
|
for b in mapping[0]['regdb']:
|
|
regdb_filename = b['data']
|
|
f = open(regdb_filename, 'rb')
|
|
data = f.read()
|
|
f.close()
|
|
|
|
self.add_regdb(data, b['names'])
|
|
|
|
return self
|
|
|
|
def validate(self):
|
|
allnames = []
|
|
|
|
for board in self.boards:
|
|
for name in board.names:
|
|
if name in allnames:
|
|
# TODO: Find a better way to report problems,
|
|
# maybe return a list of strings? Or use an
|
|
# exception?
|
|
print('Warning: duplicate board name: %s' % (name.name))
|
|
return
|
|
|
|
allnames.append(name)
|
|
|
|
def _add_signature(self, buf, offset):
|
|
signature = ATH12K_BOARD_SIGNATURE + b'\0'
|
|
length = len(signature)
|
|
pad_len = padding_needed(length)
|
|
length = length + pad_len
|
|
|
|
padding = ctypes.create_string_buffer(pad_len)
|
|
|
|
for i in range(pad_len):
|
|
struct.pack_into('<B', padding, i, PADDING_MAGIC)
|
|
|
|
fmt = '<%ds%ds' % (len(signature), pad_len)
|
|
struct.pack_into(fmt, buf, offset, signature, padding.raw)
|
|
offset += length
|
|
|
|
# make sure ATH12K_BOARD_SIGNATURE_LEN is correct
|
|
assert ATH12K_BOARD_SIGNATURE_LEN == length
|
|
|
|
return offset
|
|
|
|
@staticmethod
|
|
def open(name):
|
|
self = BoardContainer()
|
|
|
|
f = open(name, 'rb')
|
|
buf = f.read()
|
|
f.close()
|
|
buf_len = len(buf)
|
|
|
|
logging.debug('BoardContainer.open(): name %s' % (name))
|
|
|
|
offset = 0
|
|
|
|
fmt = '<%dsb' % (len(ATH12K_BOARD_SIGNATURE))
|
|
(signature, null) = struct.unpack_from(fmt, buf, offset)
|
|
|
|
if signature != ATH12K_BOARD_SIGNATURE or null != 0:
|
|
print("invalid signature found in %s" % name)
|
|
return 1
|
|
|
|
offset += ATH12K_BOARD_SIGNATURE_LEN
|
|
|
|
# looping main IEs
|
|
while offset < buf_len:
|
|
(ie_id, ie_len) = struct.unpack_from('<2i', buf, offset)
|
|
logging.debug('BoardContainer.open(): found offset %d ie_id %d ie_len %d' %
|
|
(offset, ie_id, ie_len))
|
|
|
|
offset += TYPE_LENGTH_SIZE
|
|
|
|
if offset + ie_len > buf_len:
|
|
print('Error: Buffer too short (%d + %d > %d)' % (offset,
|
|
ie_len,
|
|
buf_len))
|
|
return 1
|
|
|
|
if ie_id == ATH12K_BD_IE_BOARD:
|
|
self.boards.append(Board.parse_ie(buf, offset, ie_len))
|
|
elif ie_id == ATH12K_BD_IE_REGDB:
|
|
self.regdbs.append(Regdb.parse_ie(buf, offset, ie_len))
|
|
|
|
offset += ie_len + padding_needed(ie_len)
|
|
|
|
self.validate()
|
|
|
|
return self
|
|
|
|
def write(self, name):
|
|
(buf, buf_len) = self.get_bin()
|
|
|
|
fd = open(name, 'wb')
|
|
fd.write(buf.raw[0:buf_len])
|
|
fd.close()
|
|
|
|
self.validate()
|
|
|
|
print("board binary file '%s' is created" % name)
|
|
|
|
def get_bin(self):
|
|
buf = ctypes.create_string_buffer(MAX_BUF_LEN)
|
|
offset = 0
|
|
|
|
offset = self._add_signature(buf, offset)
|
|
|
|
for board in self.boards:
|
|
offset = board.add_to_buf(buf, offset)
|
|
|
|
for regdb in self.regdbs:
|
|
offset = regdb.add_to_buf(buf, offset)
|
|
|
|
# returns buffer and it's length
|
|
return buf, offset
|
|
|
|
def get_summary(self, sort=False):
|
|
(buf, buf_len) = self.get_bin()
|
|
|
|
s = ''
|
|
|
|
s += 'FileSize: %d\n' % (buf_len)
|
|
s += 'FileCRC32: %08x\n' % (_crc32(buf[0:buf_len]))
|
|
s += 'FileMD5: %s\n' % (hashlib.md5(buf[0:buf_len]).hexdigest())
|
|
|
|
boards = self.boards
|
|
|
|
if sort:
|
|
boards = sorted(boards, key=lambda board: board.get_names_as_str())
|
|
|
|
index = 0
|
|
for board in boards:
|
|
if not sort:
|
|
index_s = '[%d]' % (index)
|
|
else:
|
|
index_s = ''
|
|
|
|
s += 'BoardNames%s: \'%s\'\n' % (index_s, pretty_array_str(board.get_names_as_str()))
|
|
s += 'BoardLength%s: %d\n' % (index_s, len(board.data.data))
|
|
s += 'BoardCRC32%s: %08x\n' % (index_s, _crc32(board.data.data))
|
|
s += 'BoardMD5%s: %s\n' % (index_s, hashlib.md5(board.data.data).hexdigest())
|
|
index += 1
|
|
|
|
regdbs = self.regdbs
|
|
|
|
if sort:
|
|
regdbs = sorted(regdbs, key=lambda regdb: regdb.get_names_as_str())
|
|
|
|
index = 0
|
|
for regdb in regdbs:
|
|
if not sort:
|
|
index_s = '[%d]' % (index)
|
|
else:
|
|
index_s = ''
|
|
|
|
s += 'RegdbNames%s: \'%s\'\n' % (index_s, pretty_array_str(regdb.get_names_as_str()))
|
|
s += 'RegdbLength%s: %d\n' % (index_s, len(regdb.data.data))
|
|
s += 'RegdbCRC32%s: %08x\n' % (index_s, _crc32(regdb.data.data))
|
|
s += 'RegdbMD5%s: %s\n' % (index_s, hashlib.md5(regdb.data.data).hexdigest())
|
|
index += 1
|
|
|
|
return s
|
|
|
|
def __init__(self):
|
|
self.boards = []
|
|
self.regdbs = []
|
|
|
|
|
|
def cmd_extract(args):
|
|
cont = BoardContainer().open(args.extract)
|
|
|
|
mapping = []
|
|
d = {}
|
|
mapping.append(d)
|
|
|
|
mapping_board = []
|
|
d['board'] = mapping_board
|
|
|
|
for board in cont.boards:
|
|
filename = board.names[0].name + '.bin'
|
|
|
|
b = {}
|
|
b['names'] = board.get_names_as_str()
|
|
b['data'] = filename
|
|
mapping_board.append(b)
|
|
|
|
f = open(filename, 'wb')
|
|
f.write(board.data.data)
|
|
f.close()
|
|
|
|
print("%s created size: %d" % (filename, len(board.data.data)))
|
|
|
|
mapping_regdb = []
|
|
d['regdb'] = mapping_regdb
|
|
|
|
for regdb in cont.regdbs:
|
|
filename = regdb.names[0].name + '.regdb'
|
|
|
|
b = {}
|
|
b['names'] = regdb.get_names_as_str()
|
|
b['data'] = filename
|
|
mapping_regdb.append(b)
|
|
|
|
f = open(filename, 'wb')
|
|
f.write(regdb.data.data)
|
|
f.close()
|
|
|
|
print("%s created size: %d" % (filename, len(regdb.data.data)))
|
|
|
|
filename = DEFAULT_JSON_FILE
|
|
f = open(filename, 'w')
|
|
f.write(json.dumps(mapping, indent=4))
|
|
f.close()
|
|
|
|
print("%s created" % (filename))
|
|
|
|
|
|
def cmd_info(args):
|
|
filename = args.info
|
|
|
|
cont = BoardContainer().open(filename)
|
|
|
|
print(cont.get_summary())
|
|
|
|
|
|
def cmd_diff(args):
|
|
if args.diff:
|
|
filename1 = args.diff[0]
|
|
filename2 = args.diff[1]
|
|
else:
|
|
filename1 = args.diffstat[0]
|
|
filename2 = args.diffstat[1]
|
|
|
|
print(diff_boardfiles(filename1, filename2, args.diff))
|
|
|
|
|
|
def diff_boardfiles(filename1, filename2, diff):
|
|
result = ''
|
|
|
|
container1 = BoardContainer().open(filename1)
|
|
(temp1_fd, temp1_pathname) = tempfile.mkstemp()
|
|
os.write(temp1_fd, container1.get_summary(sort=True).encode())
|
|
|
|
container2 = BoardContainer().open(filename2)
|
|
(temp2_fd, temp2_pathname) = tempfile.mkstemp()
|
|
os.write(temp2_fd, container2.get_summary(sort=True).encode())
|
|
|
|
# this function is used both with --diff and --diffstat
|
|
if diff:
|
|
# '--less-mode' and '--auto-page' would be nice when running on
|
|
# terminal but don't know how to get the control character
|
|
# through. For terminal detection sys.stdout.isatty() can be used.
|
|
cmd = ['wdiff', temp1_pathname, temp2_pathname]
|
|
|
|
# wdiff is braindead and returns 1 in a succesfull case
|
|
try:
|
|
output = subprocess.check_output(cmd)
|
|
except subprocess.CalledProcessError as e:
|
|
if e.returncode == 1:
|
|
output = e.output
|
|
else:
|
|
print('Failed to run wdiff: %d\n%s' % (e.returncode, e.output))
|
|
return 1
|
|
except OSError as e:
|
|
print('Failed to run wdiff: %s' % (e))
|
|
return 1
|
|
|
|
result += '%s\n' % (output.decode())
|
|
|
|
# create simple statistics about changes in board images
|
|
new_boards = {}
|
|
deleted_boards = {}
|
|
changed_boards = {}
|
|
|
|
for board in container2.boards:
|
|
# convert the list to a string
|
|
s = pretty_array_str(board.get_names_as_str())
|
|
new_boards[s] = board
|
|
|
|
for board in container1.boards:
|
|
# convert the list to a string
|
|
names = pretty_array_str(board.get_names_as_str())
|
|
|
|
if names not in new_boards:
|
|
# board image has been deleted
|
|
deleted_boards[names] = board
|
|
continue
|
|
|
|
board2 = new_boards[names]
|
|
del new_boards[names]
|
|
|
|
if board.data.data == board2.data.data:
|
|
# board image hasn't changed
|
|
continue
|
|
|
|
# board image has changed
|
|
changed_boards[names] = board2
|
|
|
|
result += 'New board:\n%s\n\n' % ('\n'.join(list(new_boards.keys())))
|
|
result += 'Changed board:\n%s\n\n' % ('\n'.join(list(changed_boards.keys())))
|
|
result += 'Deleted board:\n%s\n' % ('\n'.join(list(deleted_boards.keys())))
|
|
|
|
result += '%d board image(s) added, %d changed, %d deleted, %d in total\n\n' % (len(new_boards),
|
|
len(changed_boards),
|
|
len(deleted_boards),
|
|
len(container2.boards))
|
|
|
|
# create simple statistics about changes in regdb images
|
|
new_regdbs = {}
|
|
deleted_regdbs = {}
|
|
changed_regdbs = {}
|
|
|
|
for regdb in container2.regdbs:
|
|
# convert the list to a string
|
|
s = pretty_array_str(regdb.get_names_as_str())
|
|
new_regdbs[s] = regdb
|
|
|
|
for regdb in container1.regdbs:
|
|
# convert the list to a string
|
|
names = pretty_array_str(regdb.get_names_as_str())
|
|
|
|
if names not in new_regdbs:
|
|
# regdb image has been deleted
|
|
deleted_regdbs[names] = regdb
|
|
continue
|
|
|
|
regdb2 = new_regdbs[names]
|
|
del new_regdbs[names]
|
|
|
|
if regdb.data.data == regdb2.data.data:
|
|
# regdb image hasn't changed
|
|
continue
|
|
|
|
# regdb image has changed
|
|
changed_regdbs[names] = regdb2
|
|
|
|
result += 'New regdb:\n%s\n\n' % ('\n'.join(list(new_regdbs.keys())))
|
|
result += 'Changed regdb:\n%s\n\n' % ('\n'.join(list(changed_regdbs.keys())))
|
|
result += 'Deleted regdb:\n%s\n' % ('\n'.join(list(deleted_regdbs.keys())))
|
|
|
|
result += '%d regdb image(s) added, %d changed, %d deleted, %d in total' % (len(new_regdbs),
|
|
len(changed_regdbs),
|
|
len(deleted_regdbs),
|
|
len(container2.regdbs))
|
|
|
|
os.close(temp1_fd)
|
|
os.close(temp2_fd)
|
|
|
|
return result
|
|
|
|
|
|
def cmd_create(args):
|
|
mapping_file = args.create
|
|
|
|
if args.output:
|
|
output = args.output
|
|
else:
|
|
output = DEFAULT_BOARD_FILE
|
|
|
|
cont = BoardContainer.open_json(mapping_file)
|
|
cont.write(output)
|
|
|
|
|
|
def cmd_add_board(args):
|
|
if len(args.add_board) < 3:
|
|
print('error: --add-board requires 3 or more arguments, only %d given' % (len(args.add_board)))
|
|
sys.exit(1)
|
|
|
|
board_filename = args.add_board[0]
|
|
new_filename = args.add_board[1]
|
|
new_names = args.add_board[2:]
|
|
|
|
f = open(new_filename, 'rb')
|
|
new_data = f.read()
|
|
f.close()
|
|
|
|
# copy the original file for diff
|
|
(temp_fd, temp_pathname) = tempfile.mkstemp()
|
|
shutil.copyfile(board_filename, temp_pathname)
|
|
|
|
container = BoardContainer.open(board_filename)
|
|
container.add_board(new_data, new_names)
|
|
container.write(board_filename)
|
|
|
|
print(diff_boardfiles(temp_pathname, board_filename, False))
|
|
|
|
os.remove(temp_pathname)
|
|
|
|
|
|
def main():
|
|
description = '''ath12k board-N.bin files manegement tool
|
|
|
|
ath12k-bdencoder is for creating (--create), listing (--info) and
|
|
comparing (--diff, --diffstat) ath12k board-N.bin files. The
|
|
board-N.bin is a container format which can have unlimited number of
|
|
actual board images ("board files"), each image containing one or
|
|
names which ath12k uses to find the correct image.
|
|
|
|
For creating board files you need a mapping file in JSON which
|
|
contains the names and filenames for the actual binary:
|
|
|
|
[
|
|
{
|
|
"board": [
|
|
{"names": ["AAA1", "AAAA2"], "data": "A.bin"},
|
|
{"names": ["B"], "data": "B.bin"},
|
|
{"names": ["C"], "data": "C.bin"},
|
|
],
|
|
"regdb": [
|
|
{"names": ["A"], "data": "A.regdb"}
|
|
]
|
|
}
|
|
]
|
|
|
|
In this example the board-N.bin will contain three board files which
|
|
are read from files named A.bin (using names AAA1 and AAAA2 in the
|
|
board-N.bin file), B.bin (using name B) and C.bin (using name C). The file also contains one regdb (regulatory database) from file A.regdb.
|
|
|
|
You can use --extract switch to see examples from real board-N.bin
|
|
files.
|
|
'''
|
|
|
|
parser = argparse.ArgumentParser(description=description,
|
|
formatter_class=argparse.RawTextHelpFormatter)
|
|
|
|
cmd_group = parser.add_mutually_exclusive_group(required=True)
|
|
cmd_group.add_argument("-c", "--create", metavar='JSON_FILE',
|
|
help='create board-N.bin from a mapping file in JSON format')
|
|
cmd_group.add_argument("-e", "--extract", metavar='BOARD_FILE',
|
|
help='extract board-N.bin file to a JSON mapping file and individual board files, compatible with the format used with --create command')
|
|
cmd_group.add_argument("-i", "--info", metavar='BOARD_FILE',
|
|
help='show all details about a board-N.bin file')
|
|
cmd_group.add_argument('-d', '--diff', metavar='BOARD_FILE', nargs=2,
|
|
help='show differences between two board-N.bin files')
|
|
cmd_group.add_argument('-D', '--diffstat', metavar='BOARD_FILE', nargs=2,
|
|
help='show a summary of differences between two board-N.bin files')
|
|
cmd_group.add_argument('-a', '--add-board', metavar='NAME', nargs='+',
|
|
help='add a board file to an existing board-N.bin, first argument is the filename of board-N.bin to add to, second is the filename board file (board.bin) to add and then followed by one or more arguments are names used in board-N.bin')
|
|
|
|
parser.add_argument('-v', '--verbose', action='store_true',
|
|
help='enable verbose (debug) messages')
|
|
parser.add_argument("-o", "--output", metavar="BOARD_FILE",
|
|
help='name of the output file, otherwise the default is: %s' %
|
|
(DEFAULT_BOARD_FILE))
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
if args.create:
|
|
return cmd_create(args)
|
|
elif args.extract:
|
|
return cmd_extract(args)
|
|
elif args.info:
|
|
return cmd_info(args)
|
|
elif args.diff:
|
|
return cmd_diff(args)
|
|
elif args.diffstat:
|
|
return cmd_diff(args)
|
|
elif args.add_board:
|
|
return cmd_add_board(args)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|