--- /dev/null
+#!/usr/bin/env python3
+
+import argparse
+import atexit
+import logging
+import os
+import stat
+import subprocess
+import sys
+
+__version__ = "1.0.0"
+
+Format = False
+
+Device = ""
+File = ""
+
+class Rpi3:
+ long_name = "Raspberry Pi 3"
+ pass
+
+class Rpi4:
+ long_name = "Raspberry Pi 4"
+ pass
+
+class VF2:
+ long_name = "VisionFive2"
+ pass
+
+class RV64:
+ long_name = "QEMU RISC-V 64-bit"
+ pass
+
+TARGETS = {
+ 'rpi3': Rpi3,
+ 'rpi4': Rpi4,
+ 'vf2': VF2,
+ 'rv64': RV64
+}
+
+def check_args(args):
+ logging.info(f"Device: {args.device}")
+
+ if args.binaries and len(args.binaries) > 0:
+ logging.info("Fusing binar{}: {}".format("y" if len(args.binaries) == 1 else "ies",
+ ", ".join(args.binaries)))
+
+ if args.format:
+ response = input(f"{args.device} will be formatted. Is it OK? [y/N]")
+ if response.lower() in ('y', 'yes'):
+ Format = True
+
+def check_device(args):
+ Device = args.device
+ if not os.path.exists(Device) and args.create:
+ logging.debug(f"dd if=/dev/zero of={Device} conv=sparse bs=1M count={args.size}")
+ rc = subprocess.run(["dd", "if=/dev/zero", f"of={Device}",
+ "conv=sparse", "bs=1M", f"count={args.size}"])
+ if rc.returncode != 0:
+ logging.error("Failed to create the backing file")
+ sys.exit(1)
+
+ if os.path.isfile(Device):
+ File = Device
+ logging.debug(f"losetup --show --partscan --find {File}")
+ rc = subprocess
+
+ proc = subprocess.Popen(["losetup", "--show", "--partscan",
+ "--find", f"{File}"],
+ stdout=subprocess.PIPE)
+ Device = proc.communicate()[0].decode('utf-8').strip()
+ logging.debug(f"Loop device found: {Device}")
+ atexit.register(lambda: subprocess.run(["losetup", "-d", Device]))
+
+ try:
+ s = os.stat(Device)
+ if not stat.S_ISBLK(s.st_mode):
+ raise TypeError
+ except FileNotFoundError:
+ logging.error(f"No such device: {Device}")
+ sys.exit(1)
+ except TypeError:
+ logging.error(f"{Device} is not a block device")
+ sys.exit(1)
+
+def check_partition_format():
+ pass
+
+def check_ddversion():
+ pass
+
+def fuse_image():
+ pass
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description="For {}, version {}".format(
+ ", ".join([v.long_name for k,v in TARGETS.items()]),
+ __version__
+ ))
+ parser.add_argument("-b", "--binary", action="append", dest="binaries",
+ help="binary to flash")
+ parser.add_argument("--create", action="store_true",
+ help="create the backing file and format the loopback device")
+ parser.add_argument("-d", "--device", required=True,
+ help="device node or loopback backing file")
+ parser.add_argument("--format", action="store_true",
+ help="create new partition table on the target device")
+ parser.add_argument("--log-level", dest="log_level", default="warning",
+ help="Verbosity, possible values: debug, info, warning, "
+ "error, critical (default: warning)")
+ parser.add_argument("-s", "--size", type=int, default=8192,
+ help="size of the backing file to create")
+ parser.add_argument("-t", "--target",
+ help="target device model")
+ parser.add_argument("--version", action="version",
+ version=f"%(prog)s {__version__}")
+ args = parser.parse_args()
+
+ print(repr(args))
+
+ logging.basicConfig(format='%(asctime)s.%(msecs)03d %(levelname)s:%(message)s',
+ level=args.log_level.upper())
+
+ check_args(args)
+ check_device(args)
+ check_partition_format()
+ check_ddversion()
+ fuse_image()