blob: bb267406427a6d13032faa5e59d9f3f70b2b5a49 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2022 Project CHIP Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import codecs
import sys
from intelhex import IntelHex
import argparse
import json
import logging as log
import cbor2 as cbor
HEX_PREFIX = "hex:"
class PartitionCreator:
"""
Class to create nrfconnect partition containing FactoryData
:param offset: This is a partition offset where data will be stored in device's flash memory
:param length: This is a maximum partition size
:param input: This is a path to input JSON file
:param output: This is a path to output directory
"""
def __init__(self, offset: int, length: int, input: str, output: str) -> None:
self._ih = IntelHex()
self._length = length
self._offset = offset
self._data_ready = False
self._output = output
self._input = input
try:
self.__data_to_save = self._convert_to_dict(self._load_json())
except IOError:
sys.exit(-1)
def generate_cbor(self):
"""
Generates .cbor file using cbor2 library.
It generate a CBORTag 55799 which is user-specific tag
"""
if self.__data_to_save:
# prepare raw data from Json
cbor_data = cbor.dumps(self.__data_to_save)
return cbor_data
def create_hex(self, data: bytes):
"""
Creates .hex file from CBOR.
This file can be write directly to device.
"""
if len(data) > self._length:
raise ValueError("generated CBOR file exceeds declared maximum partition size! {} > {}".format(len(data), self._length))
self._ih.putsz(self._offset, data)
self._ih.write_hex_file(self._output + ".hex", True)
self._data_ready = True
return True
def create_bin(self):
"""
Creates raw binary data of created previously .hex file
"""
if not self._data_ready:
log.error("Please create hex file first!")
return False
self._ih.tobinfile(self._output + ".bin")
return True
@staticmethod
def _convert_to_dict(data):
"""
Converts a list containing tuples ("key_name", "key_value") to a dictionary
If "key_value" of data entry is a string-type variable and contains a HEX_PREFIX algorithm decodes it
to hex format to be sure that a cbor file will contain proper bytes.
If "key_value" of data entry is a dictionary, algorithm appends it to the created dictionary.
"""
output_dict = dict()
for entry in data:
if not isinstance(entry, dict):
log.debug("Processing entry {}".format(entry))
if isinstance(data[entry], str) and data[entry].startswith(HEX_PREFIX):
output_dict[entry] = codecs.decode(data[entry][len(HEX_PREFIX):], "hex")
elif isinstance(data[entry], str):
output_dict[entry] = data[entry].encode("utf-8")
else:
output_dict[entry] = data[entry]
else:
output_dict[entry] = entry
return output_dict
def _load_json(self):
"""
Loads file containing a JSON data and converts it to JSON format
:raises IOError: if provided JSON file can not be read out.
"""
try:
with open(self._input, "rb") as json_file:
return json.loads(json_file.read())
except IOError as e:
log.error("Can not read Json file {}".format(self._input))
raise e
def print_flashing_help():
print("\nTo flash the generated hex containing factory data, run the following command:")
print("For nrf52:")
print("-------------------------------------------------------------------------------")
print("nrfjprog -f nrf52 --program HEXFILE_PATH --sectorerase")
print("-------------------------------------------------------------------------------")
print("For nrf53:")
print("-------------------------------------------------------------------------------")
print("nrfjprog -f nrf53 --program HEXFILE_PATH --sectorerase")
print("-------------------------------------------------------------------------------")
def main():
def allow_any_int(i): return int(i, 0)
parser = argparse.ArgumentParser(description="NrfConnect Factory Data NVS partition generator tool")
parser.add_argument("-i", "--input", type=str, required=True,
help="Path to input .json file")
parser.add_argument("-o", "--output", type=str, required=True,
help="Prefix for output file paths, e.g. setting dir/output causes creation of the following files: dir/output.hex, and dir/output.bin")
parser.add_argument("--offset", type=allow_any_int, required=True,
help="Partition offset - an address in device's NVM memory, where factory data will be stored")
parser.add_argument("--size", type=allow_any_int, required=True,
help="The maximum partition size")
parser.add_argument("-v", "--verbose", action="store_true",
help="Run this script with DEBUG logging level")
parser.add_argument("-r", "--raw", action="store_true",
help="Do not print flashing help and other logs, only generate a .hex file. It can be useful when the script is used by other script.")
args = parser.parse_args()
if args.verbose:
log.basicConfig(format='[%(asctime)s][%(levelname)s] %(message)s', level=log.DEBUG)
elif args.raw:
log.basicConfig(format='%(message)s', level=log.ERROR)
else:
log.basicConfig(format='[%(asctime)s] %(message)s', level=log.INFO)
partition_creator = PartitionCreator(args.offset, args.size, args.input, args.output)
cbor_data = partition_creator.generate_cbor()
try:
if not args.raw:
print("Generating .hex file: {}.hex with offset: {} and size: {}".format(args.output, hex(args.offset), hex(args.size)))
if partition_creator.create_hex(cbor_data) and partition_creator.create_bin():
if not args.raw:
print_flashing_help()
except ValueError as e:
log.error(e)
sys.exit(-1)
if __name__ == "__main__":
main()