Adding first bash of codi flashing - modified ymodem support, listing of newer server versions if available, lock file support to allow killing of codiServer

This commit is contained in:
Adam Boardman 2020-12-16 22:35:25 +00:00
parent 11d266b8fc
commit fe385178b7
8 changed files with 459 additions and 162 deletions

View file

@ -1,6 +1,7 @@
import serial
import struct
import threading
import time
import codi_st32_generated_functions as st32Cmd
isRunning = True
@ -22,30 +23,51 @@ def init():
def stop():
global isRunning
global socket
global thread
isRunning = False
socket.cancel_read()
time.sleep(0.1)
socket.close()
thread.join(4)
def getSocket():
global socket
global isRunning
global thread
global inUpload
isRunning = False
inUpload = False
if socket is not None:
socket.cancel_read()
if thread is not None:
thread.join(4)
return socket
def readFromSerial():
global socket
global isRunning
msgHeader = bytes.fromhex('58 21 58 21')
print('Listening...')
print('[115200]Listening...')
while isRunning:
header = socket.read_until(msgHeader, size=300)
# print('Found header', header)
# print('[115200]Found header', header)
# Read Size
if len(header) >= 4:
if len(header) >= 4 and isRunning and header[0:4] == msgHeader:
msgSize = struct.unpack('>I', socket.read(4))[0]
# print('Found message size', msgSize)
if msgSize <= 300:
# print('[115200]Found message size', msgSize)
if msgSize <= 300 and isRunning:
msg = socket.read(msgSize-8)
st32Cmd.readMessage(msg)
else:
if isRunning:
print('Message length wrong, ignoring msg')
print('[115200]Message length wrong, ignoring msg')
def sendCommand(cmd):
global socket
@ -57,3 +79,60 @@ def sendCommand(cmd):
lock.release()
except Exception as e:
print(e)
def uploadReadFromSerial():
global socket
global isRunning
print('[230400]Listening...')
while isRunning:
uploadResponse = socket.read()
print('[230400]Response', uploadResponse)
def switchToUploadMode():
global socket
global lock
global thread
global isRunning
try:
isRunning = False
if socket is not None:
socket.cancel_read()
time.sleep(1)
socket.close()
if thread is not None:
thread.join(4)
socket = serial.Serial('/dev/ttyS1', baudrate=230400, timeout=4)
thread = threading.Thread(target=uploadReadFromSerial)
isRunning = True
thread.start()
except Exception as e:
print(e)
def switchToCmdMode():
global socket
global lock
global thread
global isRunning
try:
isRunning = False
if socket is not None:
socket.cancel_read()
time.sleep(1)
socket.close()
if thread is not None:
thread.join(4)
socket = serial.Serial('/dev/ttyS1', baudrate=115200)
thread = threading.Thread(target=readFromSerial)
isRunning = True
thread.start()
except Exception as e:
print(e)

26
codi-app/codiReset.py Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env python3
import time
import lock_file
def stm32_hardware_reset():
print("Reset STM32 1")
with open('/proc/AEON_RESET_STM32', 'w') as f:
f.write("1")
time.sleep(1)
print("Reset STM32 0")
with open('/proc/AEON_RESET_STM32', 'w') as f:
f.write("0")
time.sleep(4)
def stm32_into_download_mode(prepare="0"):
print("STM32_DL_FW", prepare)
with open('/proc/AEON_STM32_DL_FW', 'w') as f:
f.write(prepare)
lock = "/tmp/.codi.lock"
killed = lock_file.check_and_kill(lock)
lock_file.lock(lock)
stm32_hardware_reset()
stm32_into_download_mode()

View file

@ -16,6 +16,7 @@ import signal
import CodiStatus
import EventListener
import Addressbook
import lock_file
def signalHandler(_signo, _stack_frame):
# mtkCmd.SetMouse(0, 1)
@ -27,7 +28,9 @@ signal.signal(signal.SIGTERM, signalHandler)
signal.signal(signal.SIGABRT, signalHandler)
signal.signal(signal.SIGHUP, signalHandler)
lock = "/tmp/.codi.lock"
lock_file.check_and_kill(lock)
lock_file.lock(lock)
CodiStatus.init()

172
codi-app/codiUpdate.py Executable file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env python3
import logging
import os
import time
import struct
import urllib.request
import optparse
from distutils.version import LooseVersion
from xmodem import YMODEM
import codi_st32_generated_functions as st32Cmd
import SerialPortManager
import lock_file
ospi_url = None
resources_url = None
def writeUint8(p):
return struct.pack(">B", p)
def writeUint32(p):
return struct.pack(">I", p)
def writeString(s):
return writeUint32(len(s)) + s.encode()
def sendMessage(commandId, args=None, sessionId='00 00 00 01'):
if args is None:
args = []
msgHeader = bytes.fromhex('58 21 58 21')
cmdId = writeUint32(commandId)
cmdSessionId = bytes.fromhex(sessionId)
# cmdSessionId = bytes.fromhex('00 00 00 01')
msgLength = len(msgHeader) + 4 + len(cmdId) + len(cmdSessionId)
for i in args:
msgLength += len(i)
cmd = msgHeader + writeUint32(msgLength) + cmdId + cmdSessionId
for i in args:
cmd += i
SerialPortManager.sendCommand(list(cmd))
def check_new_fota_versions_available():
global ospi_url
global resources_url
time.sleep(1) # Wait for listening thread to get started
sendMessage(st32Cmd.CMD_MTK_GET_CODI_FLASH_VERSION)
sendMessage(st32Cmd.CMD_MTK_GET_PROTOCOL_VERSION)
# download available versions - http://fota.planetcom.co.uk/stm32flash/cosmo_stm32_firmware_versions.txt
base_url = None
newest_version = None
for line in urllib.request.urlopen("http://fota.planetcom.co.uk/stm32flash/cosmo_stm32_firmware_versions.txt"):
firmware_line = line.decode('utf-8')
firmware_parts = firmware_line.split(':')
if len(firmware_parts) >= 3 and firmware_parts[0] == 'L':
base_url = firmware_parts[1] + ':' + firmware_parts[2].strip()
if len(firmware_parts) >= 8 and firmware_parts[0] == 'F':
version = firmware_parts[1].replace(',', '.').replace('V', '')
if newest_version == None or LooseVersion(version) > LooseVersion(newest_version):
newest_version = version
ospi_url = base_url+'/'+firmware_parts[2]
ospi_size = firmware_parts[3]
ospi_checksum = firmware_parts[4]
resources_url = base_url+'/'+firmware_parts[5]
resources_size = firmware_parts[6]
resources_checksum = firmware_parts[7]
time.sleep(2) # Wait for CODI to reply
print("CODI versions:", st32Cmd.get_codi_version(), st32Cmd.get_resources_version(), st32Cmd.get_protocol_major(),
st32Cmd.get_protocol_minor())
print("NewestVersion:", newest_version)
# print("BaseUrl:",base_url)
# print("OspiUrl:",base_url+'/'+ospi_url)
# print("ResourcesUrl:",base_url+'/'+resources_url)
if st32Cmd.get_codi_version() is not None and st32Cmd.get_resources_version() is not None:
print(LooseVersion(newest_version) > LooseVersion(st32Cmd.get_codi_version().replace('V', '')),
LooseVersion(newest_version) > LooseVersion(st32Cmd.get_resources_version().replace('R', '')))
return LooseVersion(newest_version) > LooseVersion(st32Cmd.get_codi_version().replace('V', '')) or \
LooseVersion(newest_version) > LooseVersion(st32Cmd.get_resources_version().replace('R', ''))
else:
return False
def stm32_hardware_reset():
print("Reset STM32 1")
with open('/proc/AEON_RESET_STM32', 'w') as f:
f.write("1")
time.sleep(1)
print("Reset STM32 0")
with open('/proc/AEON_RESET_STM32', 'w') as f:
f.write("0")
time.sleep(4)
def stm32_into_download_mode(prepare="0"):
print("STM32_DL_FW", prepare)
with open('/proc/AEON_STM32_DL_FW', 'w') as f:
f.write(prepare)
ser = None
def callback(total_packets, success_count, error_count):
print(total_packets, success_count, error_count)
def send_file(file):
global ser
time.sleep(1) # Wait for listening thread to get started
print("Switch to upload")
SerialPortManager.switchToUploadMode()
time.sleep(1)
stm32_into_download_mode("1")
time.sleep(4)
print("Sending 140 '0d oa' session 5 - requesting reset")
sendMessage(140, [writeUint8(0x0d), writeUint8(0x0a)], '00 00 00 05')
time.sleep(2)
stm32_hardware_reset()
stm32_into_download_mode()
print("Send Command 1")
SerialPortManager.sendCommand(writeString("1"))
time.sleep(1)
logging.basicConfig(filename='ymodem.log', level=logging.DEBUG)
ser = SerialPortManager.getSocket()
modem = YMODEM(ser)
print("Sending", file)
try:
print("Send completed:", modem.send([file], callback=callback))
except Exception as e:
print("Exception", e)
SerialPortManager.switchToCmdMode()
print("Finished")
parser = optparse.OptionParser(usage='%prog [filename]')
options, args = parser.parse_args()
lock = "/tmp/.codi.lock"
killed = lock_file.check_and_kill(lock)
lock_file.lock(lock)
SerialPortManager.init()
if len(args) > 0:
send_file(args[0])
if check_new_fota_versions_available():
print("")
print("Newer version available")
print("Please flash resources first")
print("R:",resources_url)
print("M:",ospi_url)
elif st32Cmd.get_codi_version() is not None:
print("Your all up to date - no new firmware")
else:
print("CODI Error getting existing version")
SerialPortManager.stop()
#if killed:
# print("Restarting codi server")
# os.system("/usr/lib/codi/codiServer.py & disown")

View file

@ -1,6 +1,27 @@
import struct
import CodiFunctions as cf
codi_version = None
resources_version = None
majorVer = None
minVer = None
def get_codi_version():
global codi_version
return codi_version
def get_resources_version():
global resources_version
return resources_version
def get_protocol_major():
global majorVer
return majorVer
def get_protocol_minor():
global minVer
return minVer
def readUint8(p):
return struct.unpack(">B", p[:1])[0], p[1:]
@ -168,6 +189,11 @@ CMD_SYNC_RIGHT_USB_OTG_STATUS = 144
CMD_ST_ENTRY_DEEP_SLEEP_STATUS = 145
def readMessage(msg):
global codi_version
global resources_version
global protocol_major
global protocol_minor
cmdId, msg = readUint32(msg)
# print("Got cmdId", cmdId)
sessionId, msg = readUint32(msg)
@ -179,6 +205,10 @@ def readMessage(msg):
try:
version, msg = readString(msg)
print("version =", version)
parts = version.split(b':')
if parts[0] == b'CODI' and len(parts[3]) > 0:
codi_version = parts[1].decode("utf-8")
resources_version = parts[2].decode("utf-8")
cf.CoDiFlashVersionInfo(version)
except Exception as e:
print(e)

38
codi-app/lock_file.py Normal file
View file

@ -0,0 +1,38 @@
import os
import time
import psutil
def check_and_kill(file):
pid = None
try:
f = open(file, 'r')
pidstr = f.read()
f.close()
pid = int(pidstr)
except Exception as e:
return False
print("Killing PID:",pid)
count = 0
while pid > 0 and psutil.pid_exists(pid):
try:
os.kill(pid, count)
if count == 9:
os.waitpid(pid, 0)
count = count + 1
time.sleep(1)
print("killed",count)
except OSError:
print("can't deliver signal", count, "to", pid)
pid = 0
os.remove(file)
return count > 0
def lock(file):
pidfd = os.open(file, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
os.write(pidfd, str(os.getpid()).encode())
os.close(pidfd)
def remove(file):
print('removed pidfile %s' % file)
os.remove(file)

35
codi-app/test_lock_file.py Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env python3
import lock_file
import os
import time
lock = "/tmp/test.lock"
try:
os.remove(lock)
print("Test file was present, unexpected")
except FileNotFoundError:
print("Test file not present as expected")
killed = lock_file.check_and_kill(lock)
print("Should be False:", killed)
pid = os.fork()
if pid == 0:
#lock and keep spinning forever
lock_file.lock(lock)
while True:
time.sleep(100)
print("Child running PID:",pid)
time.sleep(0.1)
killed = lock_file.check_and_kill(lock)
print("Should be True:", killed)
# Create stale lock file
pidfd = os.open(lock, os.O_CREAT|os.O_WRONLY|os.O_EXCL)
os.write(pidfd, "123456789".encode())
os.close(pidfd)
print("Expect: can't deliver signal 1&2 to 123456789")
killed = lock_file.check_and_kill(lock)
print("Should be False:", killed)
lock_file.lock(lock)

View file

@ -127,11 +127,15 @@ SOH = b'\x01'
STX = b'\x02'
EOT = b'\x04'
ACK = b'\x06'
ACK2 = b'\x86'
DLE = b'\x10'
NAK = b'\x15'
CAN = b'\x18'
CRC = b'C'
CAN2 = b'\x98'
CRC = b'C' #0x43
CRC2 = b'\xc3'
CRC3 = b'\x83'
ABT = b'a' #0x61 - flash fail - abort
class XMODEM(object):
'''
@ -143,25 +147,11 @@ class XMODEM(object):
>>> import serial
>>> from xmodem import XMODEM
>>> ser = serial.Serial('/dev/ttyUSB0', timeout=0) # or whatever you need
>>> def getc(size, timeout=1):
... return ser.read(size) or None
...
>>> def putc(data, timeout=1):
... return ser.write(data) or None
...
>>> modem = XMODEM(getc, putc)
>>> modem = XMODEM(ser)
:param getc: Function to retrieve bytes from a stream. The function takes
the number of bytes to read from the stream and a timeout in seconds as
parameters. It must return the bytes which were read, or ``None`` if a
timeout occured.
:type getc: callable
:param putc: Function to transmit bytes to a stream. The function takes the
bytes to be written and a timeout in seconds as parameters. It must
return the number of bytes written to the stream, or ``None`` in case of
a timeout.
:type putc: callable
:param ser: serial port to read from or write to.
:type getc: class
:param mode: XMODEM protocol mode
:type mode: string
:param pad: Padding character to make the packets match the packet size
@ -205,9 +195,8 @@ class XMODEM(object):
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
]
def __init__(self, getc, putc, mode='xmodem', pad=b'\x1a'):
self.getc = getc
self.putc = putc
def __init__(self, ser, mode='xmodem', pad=b'\x1a'):
self.ser = ser
self.mode = mode
self.pad = pad
self.log = logging.getLogger('xmodem.XMODEM')
@ -222,9 +211,9 @@ class XMODEM(object):
:type timeout: int
'''
for _ in range(count):
self.putc(CAN, timeout)
self.ser.write(CAN)
def send(self, stream, retry=16, timeout=60, quiet=False, callback=None):
def send(self, stream, retry=20, timeout=60, quiet=False, callback=None):
'''
Send a stream via the XMODEM protocol.
@ -270,7 +259,7 @@ class XMODEM(object):
crc_mode = 0
cancel = 0
while True:
char = self.getc(1)
char = self.ser.read(1)
if char:
if char == NAK:
self.log.debug('standard checksum requested (NAK).')
@ -280,7 +269,7 @@ class XMODEM(object):
self.log.debug('16-bit CRC requested (CRC).')
crc_mode = 1
break
elif char == CAN:
elif char == CAN or char == CAN2:
if not quiet:
print('received CAN', file=sys.stderr)
if cancel:
@ -305,6 +294,7 @@ class XMODEM(object):
error_count = 0
success_count = 0
total_packets = 0
header_sent = False
if self.mode == 'ymodem':
sequence = 0
filenames = stream
@ -312,7 +302,7 @@ class XMODEM(object):
sequence = 1
while True:
# build packet
if self.mode == 'ymodem' and success_count == 0:
if not header_sent:
# send packet sequence 0 containing:
# Filename Length [Modification-Date [Mode [Serial-Number]]]
# 'stream' is actually the filename
@ -337,10 +327,8 @@ class XMODEM(object):
header = self._make_send_header(header_size, sequence)
data = data.ljust(header_size, NUL)
checksum = self._make_send_checksum(crc_mode, data)
header_sent = True
else:
# happens after sending ymodem empty filename
if not stream:
return True
# normal data packet
data = stream.read(packet_size)
if not data:
@ -354,27 +342,38 @@ class XMODEM(object):
checksum = self._make_send_checksum(crc_mode, data)
# emit packet & get ACK
self.ser.write(header + data + checksum)
while True:
self.log.debug('send: block %d', sequence)
self.putc(header + data + checksum)
char = self.getc(1, timeout)
if char == ACK:
char = self.ser.read(1)
if char == CRC or char == CRC2 or char == CRC3:
if self.ser.in_waiting == 0:
self.log.debug('re-send: block %d, pks: %d', sequence, packet_size)
self.ser.write(header + data + checksum)
else:
rubbish = self.ser.read(self.ser.in_waiting-1)
self.log.error('got NAK rubbish %r for block %d', rubbish, sequence)
continue
if char == ACK or char == ACK2 or char == NAK:
success_count += 1
if callable(callback):
callback(total_packets, success_count, error_count)
error_count = 0
if self.mode == 'ymodem' and success_count == 1 and len(filename):
char = self.getc(1, timeout)
if char == DLE: # dunno why
char = self.getc(1, timeout)
if char == CRC:
break
self.log.error('send error: ymodem expected CRC; got %r for block %d',
char, sequence)
else:
break
if char == NAK:
rubbish = self.ser.read(1024)
self.log.error('got NAK rubbish %r for block %d', rubbish, sequence)
rubbish = self.ser.read(1024)
self.log.error('got NAK rubbish %r for block %d', rubbish, sequence)
rubbish = self.ser.read(1024)
self.log.error('got NAK rubbish %r for block %d', rubbish, sequence)
rubbish = self.ser.read(1024)
self.log.error('got NAK rubbish %r for block %d', rubbish, sequence)
break
if char == ABT:
self.log.debug('got abort')
return False
self.log.error('send error: expected ACK; got %r for block %d',
self.log.error('send error: expected CRC|ACK; got %r for block %d',
char, sequence)
error_count += 1
if callable(callback):
@ -382,7 +381,7 @@ class XMODEM(object):
if error_count > retry:
# excessive amounts of retransmissions requested,
# abort transfer
self.log.error('send error: NAK received %d times, '
self.log.error('send error: Unexpected received %d times, '
'aborting.', error_count)
self.abort(timeout=timeout)
return False
@ -394,26 +393,26 @@ class XMODEM(object):
while True:
self.log.debug('sending EOT, awaiting ACK')
# end of transmission
self.putc(EOT)
self.ser.write(EOT)
self.ser.write(EOT)
self.ser.write(EOT)
# An ACK should be returned
char = self.getc(1, timeout)
char = self.ser.read(1)
if char == ACK:
break
else:
self.log.error('send error: expected ACK; got %r', char)
error_count += 1
if error_count > retry:
self.log.warn('EOT was not ACKd, aborting transfer')
self.log.warning('EOT was not ACKd, aborting transfer')
self.abort(timeout=timeout)
return False
self.log.info('Transmission successful (ACK received).')
if self.mode == 'ymodem':
# YMODEM - recursively send next file
# or empty filename header to end the xfer batch.
# YMODEM - close the stream
stream.close()
return self.send(filenames, retry, timeout, quiet, callback)
return True
def _make_send_header(self, packet_size, sequence):
@ -476,22 +475,22 @@ class XMODEM(object):
self.abort(timeout=timeout)
return None
elif crc_mode and error_count < (retry // 2):
if not self.putc(CRC):
if not self.ser.write(CRC):
self.log.debug('recv error: putc failed, '
'sleeping for %d', delay)
time.sleep(delay)
error_count += 1
else:
crc_mode = 0
if not self.putc(NAK):
if not self.ser.write(NAK):
self.log.debug('recv error: putc failed, '
'sleeping for %d', delay)
time.sleep(delay)
error_count += 1
char = self.getc(1, timeout)
char = self.ser.read(1)
if char is None:
self.log.warn('recv error: getc timeout in start sequence')
self.log.warn('recv error: read timeout in start sequence')
error_count += 1
continue
elif char == SOH:
@ -500,7 +499,7 @@ class XMODEM(object):
elif char == STX:
self.log.debug('recv: STX')
break
elif char == CAN:
elif char == CAN or char == CAN2:
if cancel:
self.log.info('Transmission canceled: received 2xCAN '
'at start-sequence')
@ -532,11 +531,11 @@ class XMODEM(object):
elif char == EOT:
# We received an EOT, so send an ACK and return the
# received data length.
self.putc(ACK)
self.ser.write(ACK)
self.log.info("Transmission complete, %d bytes",
income_size)
return income_size
elif char == CAN:
elif char == CAN or char == CAN2:
# cancel at two consecutive cancels
if cancel:
self.log.info('Transmission canceled: received 2xCAN '
@ -562,15 +561,15 @@ class XMODEM(object):
error_count = 0
cancel = 0
self.log.debug('recv: data block %d', sequence)
seq1 = self.getc(1, timeout)
seq1 = self.ser.read(1)
if seq1 is None:
self.log.warn('getc failed to get first sequence byte')
self.log.warn('read failed to get first sequence byte')
seq2 = None
else:
seq1 = ord(seq1)
seq2 = self.getc(1, timeout)
seq2 = self.ser.read(1)
if seq2 is None:
self.log.warn('getc failed to get second sequence byte')
self.log.warn('read failed to get second sequence byte')
else:
# second byte is the same as first as 1's complement
seq2 = 0xff - ord(seq2)
@ -582,21 +581,21 @@ class XMODEM(object):
'got (seq1=%r, seq2=%r), '
'receiving next block, will NAK.',
sequence, seq1, seq2)
self.getc(packet_size + 1 + crc_mode)
self.ser.read(packet_size + 1 + crc_mode)
else:
# sequence is ok, read packet
# packet_size + checksum
data = self.getc(packet_size + 1 + crc_mode, timeout)
data = self.ser.read(packet_size + 1 + crc_mode)
valid, data = self._verify_recv_checksum(crc_mode, data)
# valid data, append chunk
if valid:
income_size += len(data)
stream.write(data)
self.putc(ACK)
self.ser.write(ACK)
sequence = (sequence + 1) % 0x100
# get next start-of-header byte
char = self.getc(1, timeout)
char = self.ser.read(1)
continue
# something went wrong, request retransmission
@ -610,12 +609,12 @@ class XMODEM(object):
# call the character receive subroutine, specifying a 1-second
# timeout, and looping back to PURGE until a timeout occurs.
# The <nak> is then sent, ensuring the other end will see it.
data = self.getc(1, timeout=1)
data = self.ser.read(1)
if data is None:
break
self.putc(NAK)
self.ser.write(NAK)
# get next start-of-header byte
char = self.getc(1, timeout)
char = self.ser.read(1)
continue
def _verify_recv_checksum(self, crc_mode, data):
@ -679,88 +678,3 @@ class XMODEM(object):
XMODEM1k = partial(XMODEM, mode='xmodem1k')
YMODEM = partial(XMODEM, mode='ymodem')
def run():
import optparse
import subprocess
parser = optparse.OptionParser(
usage='%prog [<options>] <send|recv> filename filename')
parser.add_option('-m', '--mode', default='xmodem',
help='XMODEM mode (xmodem, xmodem1k, ymodem)')
options, args = parser.parse_args()
if options.mode != 'ymodem' and len(args) != 3:
parser.error('invalid arguments')
return 1
elif len(args) < 2:
parser.error('invalid arguments')
return 1
elif args[0] not in ('send', 'recv'):
parser.error('invalid mode')
return 1
def _func(so, si):
import select
print(('si', si))
print(('so', so))
def getc(size, timeout=3):
read_ready, _, _ = select.select([so], [], [], timeout)
if read_ready:
data = so.read(size)
else:
data = None
print(('getc(', repr(data), ')'))
return data
def putc(data, timeout=3):
_, write_ready, _ = select.select([], [si], [], timeout)
if write_ready:
si.write(data)
si.flush()
size = len(data)
else:
size = None
print(('putc(', repr(data), repr(size), ')'))
return size
return getc, putc
def _pipe(*command):
pipe = subprocess.Popen(command,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
return pipe.stdout, pipe.stdin
if args[0] == 'recv':
getc, putc = _func(*_pipe('sz', '--xmodem', args[2]))
stream = open(args[1], 'wb')
xmodem = XMODEM(getc, putc, mode=options.mode)
status = xmodem.recv(stream, retry=8)
assert status, ('Transfer failed, status is', False)
stream.close()
elif args[0] == 'send':
rzargs = ['rz']
if options.mode == 'ymodem':
rzargs += ['--ymodem']
else:
rzargs += ['--xmodem']
rzargs += args[2]
getc, putc = _func(*_pipe(*rzargs))
if options.mode != 'ymodem':
stream = open(args[1], 'rb')
else:
stream = args[1:]
xmodem = XMODEM(getc, putc, mode=options.mode)
sent = xmodem.send(stream, retry=8)
assert sent is not None, ('Transfer failed, sent is', sent)
if options.mode != 'ymodem':
stream.close()
if __name__ == '__main__':
sys.exit(run())