Add fast flash script for sdcard switch hardware (#2480)

This commit is contained in:
sommermorgentraum 2025-01-20 19:43:57 +08:00 committed by GitHub
parent 9e1eb31150
commit c413f925a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 285 additions and 0 deletions

View File

@ -0,0 +1,265 @@
# Copyleft Whiterose from the Dark Army
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
# this tool allow you flash pp and copy the apps in fastest way. note that you need to have a sdcard switcher to switch sdcard to pc.
# although, if you don't have one, you still can take out the sdcard and plug it into your pc, but i think it would not pretty fast.
#!/usr/bin/env python3
import sys
import glob
import serial
import serial.tools.list_ports
import os
import shutil
import time
import subprocess
import tempfile
# config
SUDO_PASSWORD = ""
SDCARD_LABEL = "portapack"
def write_command(ser, command, wait_response=None, remove_echo=False, dont_wait_ch=False):
# clean buffer
while True:
try:
if ser.in_waiting:
ser.read(ser.in_waiting)
else:
break
except:
break
command_with_newline = command + "\r\n"
try:
ser.write(command_with_newline.encode())
except Exception as e:
return f"write err: {str(e)}"
response = ""
expected_echo = f"{command}\r\n"
wait_response_met = wait_response is None
while True:
try:
if ser.in_waiting:
msg = ser.read(ser.in_waiting).decode('utf-8')
response += msg
if wait_response and wait_response in response:
wait_response_met = True
if wait_response_met and (dont_wait_ch or "ch> " in response):
if remove_echo:
return response.replace(expected_echo, "")
return response
except serial.SerialTimeoutException:
continue
except Exception as e:
return f"e: {str(e)}"
def get_serial_devices():
ports = serial.tools.list_ports.comports()
if not ports:
print("no serial device found")
return None
portapack_device = None
for port in ports:
if port.product == "PortaPack Mayhem":
print(f"found your pp:")
print(f"device: {port.device}")
print(f"description: {port.description}")
print(f"hwid: {port.hwid}")
if port.manufacturer:
print(f"manufacturer: {port.manufacturer}")
if port.serial_number:
print(f"serial_number: {port.serial_number}")
portapack_device = port
break
if not portapack_device:
print("no pp device found")
return portapack_device
def run_sudo_command(cmd):
if not SUDO_PASSWORD:
print("sudo password not set, please hard code it in the script")
return
try:
process = subprocess.Popen(
['sudo', '-S'] + cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
stdout, stderr = process.communicate(input=SUDO_PASSWORD + '\n')
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, cmd, stdout, stderr)
return stdout
except subprocess.CalledProcessError as e:
print(f"sudo command failed: {e}")
raise
def get_pp_device_linux():
try:
cmd = ["lsblk", "-o", "NAME,LABEL,FSTYPE,SIZE,MOUNTPOINT", "-n", "-p"]
result = subprocess.run(cmd, capture_output=True, text=True)
# find your pp sd
for line in result.stdout.splitlines():
clean_line = line.replace('└─', '').replace('├─', '').strip()
parts = clean_line.split()
if len(parts) >= 2 and SDCARD_LABEL in parts[1]: # checker
device_path = parts[0]
# if path valid
if not os.path.exists(device_path):
continue
# if mounted, return mountpoint
if len(parts) >= 5:
mountpoint = parts[4]
return device_path, mountpoint
# if not, create temp dir and mount
mount_point = tempfile.mkdtemp()
print(f"mount {device_path} to {mount_point}")
run_sudo_command(["mount", "-o", f"uid={os.getuid()},gid={os.getgid()}", device_path, mount_point])
return device_path, mount_point
return None, None
except subprocess.CalledProcessError as e:
print(f"command failed: {str(e)}")
return None, None
def wait_for_pp_drive():
while True:
if os.name == 'nt': # win didn't test yet, TODO
drives = [d + ':' for d in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if os.path.exists(d + ':')]
for drive in drives:
if os.path.basename(os.path.abspath(drive)).lower() == SDCARD_LABEL:
return drive, None
else: # linux and mac
device_path, mount_point = get_pp_device_linux()
if device_path and mount_point:
return mount_point, device_path
time.sleep(1)
def run():
# worker 1: check device connected
device = get_serial_devices()
if not device:
print("no pp device found")
return
try:
# worker 2: open serial and send hackrf cmd
ser = serial.Serial(device.device, baudrate=115200, timeout=1)
for i in range(2):
write_command(ser, "hackrf")
ser.close()
# worker 3: hint user to swtich sdcard to pc
print("\033[92m\n\t+------------------------+")
print("\t| switch PP sdcard to PC |")
print("\t+------------------------+\033[0m")
print("wait PP sdcard...")
# worker 4: wait pp sd
mount_point, device_path = wait_for_pp_drive()
if not mount_point:
print("no pp sd found")
return
print(f"find pp sd: {mount_point}")
# worker 5: copy ppma and ppmp apps
apps_dir = os.path.join(mount_point, 'APPS')
if not os.path.exists(apps_dir):
os.makedirs(apps_dir)
firmware_dir = os.path.join('.', 'firmware', 'application')
for ext in ['ppma', 'ppmp']:
for file in glob.glob(os.path.join(firmware_dir, f'*.{ext}')):
dest = os.path.join(apps_dir, os.path.basename(file))
print(f"cp: {os.path.basename(file)}")
shutil.copy2(file, dest)
print("apps copy done")
# worker 6: umount pp sd
if os.name != 'nt' and device_path:
run_sudo_command(["umount", mount_point])
os.rmdir(mount_point) # 删除临时挂载点
# worker 7: run flash cmd with hackrf_spiflash util
print("\nflash...")
process = subprocess.Popen(
["hackrf_spiflash", "-w", "./firmware/portapack-mayhem-firmware.bin"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True
)
while True:
output = process.stdout.readline()
if output:
print(output.strip())
if process.poll() is not None:
break
if process.returncode != 0:
raise subprocess.CalledProcessError(process.returncode, "hackrf_spiflash")
# worker 8: hint user to swtich sdcard switcher back to pp
print("\033[92m\n\t+------------------------------------------------+")
print("\t| switch sdcard switcher back to pp, then reboot |")
print("\t+------------------------------------------------+\033[0m")
except serial.SerialException as e:
print(f"serial comm error: {str(e)}")
except OSError as e:
print(f"file operation error: {str(e)}")
except subprocess.CalledProcessError as e:
print(f"system command error: {str(e)}")
except Exception as e:
print(f"error: {str(e)}")
# ensure umount when error
if os.name != 'nt' and 'mount_point' in locals() and device_path:
try:
run_sudo_command(["umount", mount_point])
os.rmdir(mount_point)
except:
pass
if __name__ == "__main__":
try:
run()
except Exception as e:
print(f"error: {str(e)}")
sys.exit(1)

View File

@ -161,3 +161,23 @@ write_image(spi_image, output_path)
percent_remaining = round(1000 * pad_size / spi_size) / 10;
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
# copy the fast flash script
build_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'build')
flash_py_path = os.path.join(build_dir, 'flash.py')
if not os.path.exists(flash_py_path):
current_dir = os.path.dirname(__file__)
source_file = os.path.join(current_dir, 'fast_flash_pp_and_copy_apps.py')
if not os.path.exists(build_dir):
os.makedirs(build_dir)
# cp
import shutil
print(f"\ncopy {source_file} to {flash_py_path}\n")
shutil.copy2(source_file, flash_py_path)