mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-02-09 19:28:40 -05:00
Add fast flash script for sdcard switch hardware (#2480)
This commit is contained in:
parent
9e1eb31150
commit
c413f925a9
265
firmware/tools/fast_flash_pp_and_copy_apps.py
Normal file
265
firmware/tools/fast_flash_pp_and_copy_apps.py
Normal 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)
|
@ -161,3 +161,23 @@ write_image(spi_image, output_path)
|
|||||||
|
|
||||||
percent_remaining = round(1000 * pad_size / spi_size) / 10;
|
percent_remaining = round(1000 * pad_size / spi_size) / 10;
|
||||||
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user