diff --git a/codi-app/SerialPortManager.py b/codi-app/SerialPortManager.py index 25f7568..7b2e441 100644 --- a/codi-app/SerialPortManager.py +++ b/codi-app/SerialPortManager.py @@ -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) + diff --git a/codi-app/codiReset.py b/codi-app/codiReset.py new file mode 100755 index 0000000..dce2b52 --- /dev/null +++ b/codi-app/codiReset.py @@ -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() diff --git a/codi-app/codiServer.py b/codi-app/codiServer.py index 49bf11a..700a6a6 100755 --- a/codi-app/codiServer.py +++ b/codi-app/codiServer.py @@ -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() diff --git a/codi-app/codiUpdate.py b/codi-app/codiUpdate.py new file mode 100755 index 0000000..ab65225 --- /dev/null +++ b/codi-app/codiUpdate.py @@ -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") diff --git a/codi-app/codi_st32_generated_functions.py b/codi-app/codi_st32_generated_functions.py index 320f5dd..e0666f8 100644 --- a/codi-app/codi_st32_generated_functions.py +++ b/codi-app/codi_st32_generated_functions.py @@ -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) diff --git a/codi-app/lock_file.py b/codi-app/lock_file.py new file mode 100644 index 0000000..c8d23a1 --- /dev/null +++ b/codi-app/lock_file.py @@ -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) diff --git a/codi-app/test_lock_file.py b/codi-app/test_lock_file.py new file mode 100755 index 0000000..60f4181 --- /dev/null +++ b/codi-app/test_lock_file.py @@ -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) diff --git a/codi-app/xmodem/__init__.py b/codi-app/xmodem/__init__.py index 582d92d..8123f7b 100644 --- a/codi-app/xmodem/__init__.py +++ b/codi-app/xmodem/__init__.py @@ -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 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 [] 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())