1# Copyright (c) 2025 Pete Johanson 2# 3# SPDX-License-Identifier: Apache-2.0 4# 5# pylint: disable=duplicate-code 6 7'''Runner for rfp.''' 8 9import platform 10import re 11from pathlib import Path 12 13from runners.core import RunnerCaps, ZephyrBinaryRunner 14 15if platform.system() == 'Darwin' or 'Windows': 16 DEFAULT_RFP_PORT = None 17else: 18 DEFAULT_RFP_PORT = '/dev/ttyACM0' 19 20RFP_CLI_EXE = 'rfp-cli' 21 22 23def to_num(number): 24 dev_match = re.search(r"^\d*\+dev", number) 25 dev_version = dev_match is not None 26 27 num_match = re.search(r"^\d*", number) 28 num = int(num_match.group(0)) 29 30 if dev_version: 31 num += 1 32 33 return num 34 35 36class RfpBinaryRunner(ZephyrBinaryRunner): 37 '''Runner front-end for rfp.''' 38 39 def __init__( 40 self, 41 cfg, 42 rfp_cli='rfp-cli', 43 device=None, 44 erase=False, 45 verify=False, 46 port=DEFAULT_RFP_PORT, 47 tool=None, 48 interface=None, 49 speed=None, 50 ): 51 super().__init__(cfg) 52 53 self.rfp_cmd = [rfp_cli] 54 self.verify = verify 55 self.erase = erase 56 self.port = port 57 self.tool = tool 58 self.interface = interface 59 self.device = device 60 self.speed = speed 61 62 @classmethod 63 def name(cls): 64 return 'rfp' 65 66 @classmethod 67 def capabilities(cls): 68 return RunnerCaps(commands={'flash'}, erase=True) 69 70 @classmethod 71 def do_add_parser(cls, parser): 72 # Find the default efp-cli executable 73 cls.default_rfp() 74 75 parser.add_argument( 76 '--rfp-cli', default=RFP_CLI_EXE, help='path to rfp-cli, default is rfp-cli' 77 ) 78 parser.add_argument( 79 '--port', 80 default=DEFAULT_RFP_PORT, 81 help='serial port to use, default is ' + str(DEFAULT_RFP_PORT), 82 ) 83 parser.add_argument( 84 '--tool', 85 help='emulator hardware to use (e2, e2l, jlink) when port is not set', 86 ) 87 parser.add_argument( 88 '--interface', 89 help='selects the communications interface (uart, swd)', 90 ) 91 parser.add_argument('--device', help='Specify the device type to pass to rfp-cli') 92 parser.add_argument('--verify', action='store_true', help='if given, verify after flash') 93 parser.add_argument('--speed', help='Specify the serial port speed') 94 95 @classmethod 96 def do_create(cls, cfg, args): 97 return RfpBinaryRunner( 98 cfg, 99 rfp_cli=args.rfp_cli, 100 device=args.device, 101 port=args.port, 102 tool=args.tool, 103 interface=args.interface, 104 erase=args.erase, 105 speed=args.speed, 106 verify=args.verify, 107 ) 108 109 @staticmethod 110 def default_rfp(): 111 global RFP_CLI_EXE 112 113 if platform.system() == 'Windows': 114 try: 115 import winreg 116 117 registry = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) 118 key = winreg.OpenKey(registry, r"SOFTWARE\Classes\rpjfile\shell\Open\command") 119 val = winreg.QueryValue(key, None) 120 match = re.match(r'"(.*?)".*', val)[1] 121 RFP_CLI_EXE = str(Path(match).parent / 'rfp-cli.exe') 122 except Exception: 123 RFP_CLI_EXE = 'rfp-cli.exe' 124 else: 125 RFP_CLI_EXE = 'rfp-cli' 126 127 def do_run(self, command, **kwargs): 128 if command == 'flash': 129 self.do_flash(**kwargs) 130 else: 131 self.logger.error("Unsuppported command") 132 133 def do_flash(self, **kwargs): 134 self.require(self.rfp_cmd[0]) 135 self.ensure_output('hex') 136 137 hex_name = self.cfg.hex_file 138 139 self.logger.info(f'Flashing file: {hex_name}') 140 141 load_image = ['-run'] 142 if self.erase: 143 load_image += ['-erase'] 144 else: 145 load_image += ['-noerase'] 146 147 if self.verify: 148 load_image += ['-v'] 149 150 # Load image 151 load_image += ['-p', '-file', hex_name] 152 153 if self.tool is None: 154 connection = ['-port', self.port] 155 else: 156 connection = ['-tool', self.tool] 157 158 if self.interface: 159 connection += ['-interface', self.interface] 160 161 if self.speed: 162 connection += ['-s', self.speed] 163 164 device = ['-device', self.device] 165 166 cmd = self.rfp_cmd + connection + device + load_image 167 self.check_call(cmd) 168