Tidied up the ymodem implementation, as we are only testing sending a single file at a time and no attempt is being made to keep compatibility with other *modem implmentations slimmed it down to code that is actually in use.
This commit is contained in:
parent
fe385178b7
commit
afcfde4b7b
2 changed files with 54 additions and 358 deletions
|
|
@ -135,7 +135,7 @@ def send_file(file):
|
||||||
|
|
||||||
print("Sending", file)
|
print("Sending", file)
|
||||||
try:
|
try:
|
||||||
print("Send completed:", modem.send([file], callback=callback))
|
print("Send completed:", modem.send(file, callback=callback))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Exception", e)
|
print("Exception", e)
|
||||||
SerialPortManager.switchToCmdMode()
|
SerialPortManager.switchToCmdMode()
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,19 @@
|
||||||
'''
|
'''
|
||||||
===============================
|
=======================================
|
||||||
XMODEM file transfer protocol
|
Modified YMODEM file transfer protocol
|
||||||
===============================
|
=======================================
|
||||||
|
|
||||||
.. $Id$
|
.. $Id$
|
||||||
|
|
||||||
This is a literal implementation of XMODEM.TXT_, XMODEM1K.TXT_ and
|
This is a implementation of YMODEM as used on the Cosmo Communicator.
|
||||||
XMODMCRC.TXT_, support for YMODEM and ZMODEM is pending. YMODEM should
|
|
||||||
be fairly easy to implement as it is a hack on top of the XMODEM
|
|
||||||
protocol using sequence bytes ``0x00`` for sending file names (and some
|
|
||||||
meta data).
|
|
||||||
|
|
||||||
.. _XMODEM.TXT: doc/XMODEM.TXT
|
Data flow example
|
||||||
.. _XMODEM1K.TXT: doc/XMODEM1K.TXT
|
=================
|
||||||
.. _XMODMCRC.TXT: doc/XMODMCRC.TXT
|
|
||||||
|
|
||||||
Data flow example including error recovery
|
|
||||||
==========================================
|
|
||||||
|
|
||||||
Here is a sample of the data flow, sending a 3-block message.
|
Here is a sample of the data flow, sending a 3-block message.
|
||||||
It includes the two most common line hits - a garbaged block,
|
|
||||||
and an ``ACK`` reply getting garbaged. ``CRC`` or ``CSUM`` represents
|
|
||||||
the checksum bytes.
|
|
||||||
|
|
||||||
XMODEM 128 byte blocks
|
Batch Transmission Session (1 file - not tested with more than one)
|
||||||
----------------------
|
-------------------------------------------------------------------
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
SENDER RECEIVER
|
|
||||||
|
|
||||||
<-- NAK
|
|
||||||
SOH 01 FE Data[128] CSUM -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 02 FD Data[128] CSUM -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 03 FC Data[128] CSUM -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 04 FB Data[128] CSUM -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 05 FA Data[100] CPMEOF[28] CSUM -->
|
|
||||||
<-- ACK
|
|
||||||
EOT -->
|
|
||||||
<-- ACK
|
|
||||||
|
|
||||||
XMODEM-1k blocks, CRC mode
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
SENDER RECEIVER
|
|
||||||
|
|
||||||
<-- C
|
|
||||||
STX 01 FE Data[1024] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
STX 02 FD Data[1024] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
STX 03 FC Data[1000] CPMEOF[24] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
EOT -->
|
|
||||||
<-- ACK
|
|
||||||
|
|
||||||
Mixed 1024 and 128 byte Blocks
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
SENDER RECEIVER
|
|
||||||
|
|
||||||
<-- C
|
|
||||||
STX 01 FE Data[1024] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
STX 02 FD Data[1024] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 03 FC Data[128] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
SOH 04 FB Data[100] CPMEOF[28] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
EOT -->
|
|
||||||
<-- ACK
|
|
||||||
|
|
||||||
YMODEM Batch Transmission Session (1 file)
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|
@ -89,21 +21,37 @@ YMODEM Batch Transmission Session (1 file)
|
||||||
<-- C (command:rb)
|
<-- C (command:rb)
|
||||||
SOH 00 FF foo.c NUL[123] CRC CRC -->
|
SOH 00 FF foo.c NUL[123] CRC CRC -->
|
||||||
<-- ACK
|
<-- ACK
|
||||||
|
SOH 01 FE Data[1024] CRC CRC --> (probably received garbled)
|
||||||
<-- C
|
<-- C
|
||||||
SOH 01 FE Data[128] CRC CRC -->
|
SOH 01 FE Data[1024] CRC CRC (resend) --> (starts flash erase)
|
||||||
|
<-- (empty read timed out)
|
||||||
|
<-- (empty read timed out)
|
||||||
|
<-- (empty read timed out)
|
||||||
|
<-- C
|
||||||
|
SOH 01 FE Data[1024] CRC CRC (resend) -->
|
||||||
<-- ACK
|
<-- ACK
|
||||||
SOH 02 FC Data[128] CRC CRC -->
|
SOH 02 FC Data[1024] CRC CRC --> (probably received garbled)
|
||||||
|
<-- C
|
||||||
|
SOH 02 FC Data[1024] CRC CRC (resend) -->
|
||||||
<-- ACK
|
<-- ACK
|
||||||
SOH 03 FB Data[100] CPMEOF[28] CRC CRC -->
|
SOH 03 FB Data[1000] CPMEOF[24] CRC CRC -->
|
||||||
<-- ACK
|
<-- ACK
|
||||||
EOT -->
|
EOT -->
|
||||||
<-- NAK
|
EOT -->
|
||||||
|
EOT -->
|
||||||
|
<-- C
|
||||||
|
EOT -->
|
||||||
|
EOT -->
|
||||||
|
EOT -->
|
||||||
|
<-- C
|
||||||
|
EOT -->
|
||||||
|
EOT -->
|
||||||
|
EOT -->
|
||||||
|
<-- C
|
||||||
|
EOT -->
|
||||||
|
EOT -->
|
||||||
EOT -->
|
EOT -->
|
||||||
<-- ACK
|
<-- ACK
|
||||||
<-- C
|
|
||||||
SOH 00 FF NUL[128] CRC CRC -->
|
|
||||||
<-- ACK
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
from __future__ import division, print_function
|
from __future__ import division, print_function
|
||||||
|
|
@ -119,7 +67,7 @@ import platform
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
from functools import partial
|
import os
|
||||||
|
|
||||||
# Protocol bytes
|
# Protocol bytes
|
||||||
NUL = b'\x00'
|
NUL = b'\x00'
|
||||||
|
|
@ -137,22 +85,21 @@ CRC2 = b'\xc3'
|
||||||
CRC3 = b'\x83'
|
CRC3 = b'\x83'
|
||||||
ABT = b'a' #0x61 - flash fail - abort
|
ABT = b'a' #0x61 - flash fail - abort
|
||||||
|
|
||||||
class XMODEM(object):
|
class YMODEM(object):
|
||||||
'''
|
'''
|
||||||
XMODEM Protocol handler, expects two callables which encapsulate the read
|
YMODEM Protocol handler, expects two callables which encapsulate the read
|
||||||
and write operations on the underlying stream.
|
and write operations on the underlying stream.
|
||||||
|
|
||||||
Example functions for reading and writing to a serial line:
|
Example functions for reading and writing to a serial line:
|
||||||
|
|
||||||
>>> import serial
|
>>> import serial
|
||||||
>>> from xmodem import XMODEM
|
>>> from xmodem import YMODEM
|
||||||
>>> ser = serial.Serial('/dev/ttyUSB0', timeout=0) # or whatever you need
|
>>> ser = serial.Serial('/dev/ttyUSB0', timeout=0) # or whatever you need
|
||||||
>>> modem = XMODEM(ser)
|
>>> modem = YMODEM(ser)
|
||||||
|
|
||||||
|
|
||||||
:param ser: serial port to read from or write to.
|
:param ser: serial port to read from or write to.
|
||||||
:type getc: class
|
:type getc: class
|
||||||
:param mode: XMODEM protocol mode
|
|
||||||
:type mode: string
|
:type mode: string
|
||||||
:param pad: Padding character to make the packets match the packet size
|
:param pad: Padding character to make the packets match the packet size
|
||||||
:type pad: char
|
:type pad: char
|
||||||
|
|
@ -195,11 +142,10 @@ class XMODEM(object):
|
||||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
|
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, ser, mode='xmodem', pad=b'\x1a'):
|
def __init__(self, ser, pad=b'\x1a'):
|
||||||
self.ser = ser
|
self.ser = ser
|
||||||
self.mode = mode
|
|
||||||
self.pad = pad
|
self.pad = pad
|
||||||
self.log = logging.getLogger('xmodem.XMODEM')
|
self.log = logging.getLogger('codiUpdate')
|
||||||
|
|
||||||
def abort(self, count=2, timeout=60):
|
def abort(self, count=2, timeout=60):
|
||||||
'''
|
'''
|
||||||
|
|
@ -213,12 +159,11 @@ class XMODEM(object):
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
self.ser.write(CAN)
|
self.ser.write(CAN)
|
||||||
|
|
||||||
def send(self, stream, retry=20, timeout=60, quiet=False, callback=None):
|
def send(self, filename, retry=20, timeout=60, quiet=False, callback=None):
|
||||||
'''
|
'''
|
||||||
Send a stream via the XMODEM protocol.
|
Send a stream via the YMODEM protocol.
|
||||||
|
|
||||||
>>> stream = open('/etc/issue', 'rb')
|
>>> print(modem.send('filename'))
|
||||||
>>> print(modem.send(stream))
|
|
||||||
True
|
True
|
||||||
|
|
||||||
Returns ``True`` upon successful transmission or ``False`` in case of
|
Returns ``True`` upon successful transmission or ``False`` in case of
|
||||||
|
|
@ -236,7 +181,7 @@ class XMODEM(object):
|
||||||
:type quiet: bool
|
:type quiet: bool
|
||||||
:param callback: Reference to a callback function that has the
|
:param callback: Reference to a callback function that has the
|
||||||
following signature. This is useful for
|
following signature. This is useful for
|
||||||
getting status updates while a xmodem
|
getting status updates while a ymodem
|
||||||
transfer is underway.
|
transfer is underway.
|
||||||
Expected callback signature:
|
Expected callback signature:
|
||||||
def callback(total_packets, success_count, error_count)
|
def callback(total_packets, success_count, error_count)
|
||||||
|
|
@ -244,15 +189,7 @@ class XMODEM(object):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# initialize protocol
|
# initialize protocol
|
||||||
try:
|
packet_size = 1024
|
||||||
packet_size = dict(
|
|
||||||
xmodem = 128,
|
|
||||||
xmodem1k = 1024,
|
|
||||||
ymodem = 1024,
|
|
||||||
)[self.mode]
|
|
||||||
except KeyError:
|
|
||||||
raise ValueError("Invalid mode specified: {self.mode!r}"
|
|
||||||
.format(self=self))
|
|
||||||
|
|
||||||
self.log.debug('Begin start sequence, packet_size=%d', packet_size)
|
self.log.debug('Begin start sequence, packet_size=%d', packet_size)
|
||||||
error_count = 0
|
error_count = 0
|
||||||
|
|
@ -295,30 +232,18 @@ class XMODEM(object):
|
||||||
success_count = 0
|
success_count = 0
|
||||||
total_packets = 0
|
total_packets = 0
|
||||||
header_sent = False
|
header_sent = False
|
||||||
if self.mode == 'ymodem':
|
sequence = 0
|
||||||
sequence = 0
|
stream = None
|
||||||
filenames = stream
|
|
||||||
else:
|
|
||||||
sequence = 1
|
|
||||||
while True:
|
while True:
|
||||||
# build packet
|
# build packet
|
||||||
if not header_sent:
|
if not header_sent:
|
||||||
# send packet sequence 0 containing:
|
# send packet sequence 0 containing:
|
||||||
# Filename Length [Modification-Date [Mode [Serial-Number]]]
|
# Filename Length [Modification-Date [Mode [Serial-Number]]]
|
||||||
# 'stream' is actually the filename
|
stream = open(filename, 'rb')
|
||||||
import os
|
stat = os.stat(filename)
|
||||||
if len(filenames):
|
data = os.path.basename(filename).encode() + NUL + str(stat.st_size).encode()
|
||||||
filename = filenames.pop()
|
self.log.debug('ymodem sending : "%s" len:%d', filename, stat.st_size)
|
||||||
stream = open(filename, 'rb')
|
|
||||||
stat = os.stat(filename)
|
|
||||||
data = os.path.basename(filename).encode() + NUL + str(stat.st_size).encode()
|
|
||||||
self.log.debug('ymodem sending : "%s" len:%d', filename, stat.st_size)
|
|
||||||
else:
|
|
||||||
# empty file name packet terminates transmission
|
|
||||||
filename = ''
|
|
||||||
data = ''.encode()
|
|
||||||
stream = None
|
|
||||||
self.log.debug('ymodem done, sending empty header.')
|
|
||||||
if len(data) <= 128:
|
if len(data) <= 128:
|
||||||
header_size = 128
|
header_size = 128
|
||||||
else:
|
else:
|
||||||
|
|
@ -410,9 +335,7 @@ class XMODEM(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.log.info('Transmission successful (ACK received).')
|
self.log.info('Transmission successful (ACK received).')
|
||||||
if self.mode == 'ymodem':
|
stream.close()
|
||||||
# YMODEM - close the stream
|
|
||||||
stream.close()
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _make_send_header(self, packet_size, sequence):
|
def _make_send_header(self, packet_size, sequence):
|
||||||
|
|
@ -426,238 +349,13 @@ class XMODEM(object):
|
||||||
return bytearray(_bytes)
|
return bytearray(_bytes)
|
||||||
|
|
||||||
def _make_send_checksum(self, crc_mode, data):
|
def _make_send_checksum(self, crc_mode, data):
|
||||||
|
assert crc_mode is 1
|
||||||
_bytes = []
|
_bytes = []
|
||||||
if crc_mode:
|
if crc_mode:
|
||||||
crc = self.calc_crc(data)
|
crc = self.calc_crc(data)
|
||||||
_bytes.extend([crc >> 8, crc & 0xff])
|
_bytes.extend([crc >> 8, crc & 0xff])
|
||||||
else:
|
|
||||||
crc = self.calc_checksum(data)
|
|
||||||
_bytes.append(crc)
|
|
||||||
return bytearray(_bytes)
|
return bytearray(_bytes)
|
||||||
|
|
||||||
def recv(self, stream, crc_mode=1, retry=16, timeout=60, delay=1, quiet=0):
|
|
||||||
'''
|
|
||||||
Receive a stream via the XMODEM protocol.
|
|
||||||
|
|
||||||
>>> stream = open('/etc/issue', 'wb')
|
|
||||||
>>> print(modem.recv(stream))
|
|
||||||
2342
|
|
||||||
|
|
||||||
Returns the number of bytes received on success or ``None`` in case of
|
|
||||||
failure.
|
|
||||||
|
|
||||||
:param stream: The stream object to write data to.
|
|
||||||
:type stream: stream (file, etc.)
|
|
||||||
:param crc_mode: XMODEM CRC mode
|
|
||||||
:type crc_mode: int
|
|
||||||
:param retry: The maximum number of times to try to resend a failed
|
|
||||||
packet before failing.
|
|
||||||
:type retry: int
|
|
||||||
:param timeout: The number of seconds to wait for a response before
|
|
||||||
timing out.
|
|
||||||
:type timeout: int
|
|
||||||
:param delay: The number of seconds to wait between resend attempts
|
|
||||||
:type delay: int
|
|
||||||
:param quiet: If ``True``, write transfer information to stderr.
|
|
||||||
:type quiet: bool
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
# initiate protocol
|
|
||||||
error_count = 0
|
|
||||||
char = 0
|
|
||||||
cancel = 0
|
|
||||||
while True:
|
|
||||||
# first try CRC mode, if this fails,
|
|
||||||
# fall back to checksum mode
|
|
||||||
if error_count >= retry:
|
|
||||||
self.log.info('error_count reached %d, aborting.', retry)
|
|
||||||
self.abort(timeout=timeout)
|
|
||||||
return None
|
|
||||||
elif crc_mode and error_count < (retry // 2):
|
|
||||||
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.ser.write(NAK):
|
|
||||||
self.log.debug('recv error: putc failed, '
|
|
||||||
'sleeping for %d', delay)
|
|
||||||
time.sleep(delay)
|
|
||||||
error_count += 1
|
|
||||||
|
|
||||||
char = self.ser.read(1)
|
|
||||||
if char is None:
|
|
||||||
self.log.warn('recv error: read timeout in start sequence')
|
|
||||||
error_count += 1
|
|
||||||
continue
|
|
||||||
elif char == SOH:
|
|
||||||
self.log.debug('recv: SOH')
|
|
||||||
break
|
|
||||||
elif char == STX:
|
|
||||||
self.log.debug('recv: STX')
|
|
||||||
break
|
|
||||||
elif char == CAN or char == CAN2:
|
|
||||||
if cancel:
|
|
||||||
self.log.info('Transmission canceled: received 2xCAN '
|
|
||||||
'at start-sequence')
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
self.log.debug('cancellation at start sequence.')
|
|
||||||
cancel = 1
|
|
||||||
else:
|
|
||||||
error_count += 1
|
|
||||||
|
|
||||||
# read data
|
|
||||||
error_count = 0
|
|
||||||
income_size = 0
|
|
||||||
packet_size = 128
|
|
||||||
sequence = 1
|
|
||||||
cancel = 0
|
|
||||||
while True:
|
|
||||||
while True:
|
|
||||||
if char == SOH:
|
|
||||||
if packet_size != 128:
|
|
||||||
self.log.debug('recv: SOH, using 128b packet_size')
|
|
||||||
packet_size = 128
|
|
||||||
break
|
|
||||||
elif char == STX:
|
|
||||||
if packet_size != 1024:
|
|
||||||
self.log.debug('recv: SOH, using 1k packet_size')
|
|
||||||
packet_size = 1024
|
|
||||||
break
|
|
||||||
elif char == EOT:
|
|
||||||
# We received an EOT, so send an ACK and return the
|
|
||||||
# received data length.
|
|
||||||
self.ser.write(ACK)
|
|
||||||
self.log.info("Transmission complete, %d bytes",
|
|
||||||
income_size)
|
|
||||||
return income_size
|
|
||||||
elif char == CAN or char == CAN2:
|
|
||||||
# cancel at two consecutive cancels
|
|
||||||
if cancel:
|
|
||||||
self.log.info('Transmission canceled: received 2xCAN '
|
|
||||||
'at block %d', sequence)
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
self.log.debug('cancellation at block %d', sequence)
|
|
||||||
cancel = 1
|
|
||||||
else:
|
|
||||||
err_msg = ('recv error: expected SOH, EOT; '
|
|
||||||
'got {0!r}'.format(char))
|
|
||||||
if not quiet:
|
|
||||||
print(err_msg, file=sys.stderr)
|
|
||||||
self.log.warn(err_msg)
|
|
||||||
error_count += 1
|
|
||||||
if error_count > retry:
|
|
||||||
self.log.info('error_count reached %d, aborting.',
|
|
||||||
retry)
|
|
||||||
self.abort()
|
|
||||||
return None
|
|
||||||
|
|
||||||
# read sequence
|
|
||||||
error_count = 0
|
|
||||||
cancel = 0
|
|
||||||
self.log.debug('recv: data block %d', sequence)
|
|
||||||
seq1 = self.ser.read(1)
|
|
||||||
if seq1 is None:
|
|
||||||
self.log.warn('read failed to get first sequence byte')
|
|
||||||
seq2 = None
|
|
||||||
else:
|
|
||||||
seq1 = ord(seq1)
|
|
||||||
seq2 = self.ser.read(1)
|
|
||||||
if seq2 is None:
|
|
||||||
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)
|
|
||||||
|
|
||||||
if not (seq1 == seq2 == sequence):
|
|
||||||
# consume data anyway ... even though we will discard it,
|
|
||||||
# it is not the sequence we expected!
|
|
||||||
self.log.error('expected sequence %d, '
|
|
||||||
'got (seq1=%r, seq2=%r), '
|
|
||||||
'receiving next block, will NAK.',
|
|
||||||
sequence, seq1, seq2)
|
|
||||||
self.ser.read(packet_size + 1 + crc_mode)
|
|
||||||
else:
|
|
||||||
# sequence is ok, read packet
|
|
||||||
# packet_size + checksum
|
|
||||||
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.ser.write(ACK)
|
|
||||||
sequence = (sequence + 1) % 0x100
|
|
||||||
# get next start-of-header byte
|
|
||||||
char = self.ser.read(1)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# something went wrong, request retransmission
|
|
||||||
self.log.warn('recv error: purge, requesting retransmission (NAK)')
|
|
||||||
while True:
|
|
||||||
# When the receiver wishes to <nak>, it should call a "PURGE"
|
|
||||||
# subroutine, to wait for the line to clear. Recall the sender
|
|
||||||
# tosses any characters in its UART buffer immediately upon
|
|
||||||
# completing sending a block, to ensure no glitches were mis-
|
|
||||||
# interpreted. The most common technique is for "PURGE" to
|
|
||||||
# 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.ser.read(1)
|
|
||||||
if data is None:
|
|
||||||
break
|
|
||||||
self.ser.write(NAK)
|
|
||||||
# get next start-of-header byte
|
|
||||||
char = self.ser.read(1)
|
|
||||||
continue
|
|
||||||
|
|
||||||
def _verify_recv_checksum(self, crc_mode, data):
|
|
||||||
if crc_mode:
|
|
||||||
_checksum = bytearray(data[-2:])
|
|
||||||
their_sum = (_checksum[0] << 8) + _checksum[1]
|
|
||||||
data = data[:-2]
|
|
||||||
|
|
||||||
our_sum = self.calc_crc(data)
|
|
||||||
valid = bool(their_sum == our_sum)
|
|
||||||
if not valid:
|
|
||||||
self.log.warn('recv error: checksum fail '
|
|
||||||
'(theirs=%04x, ours=%04x), ',
|
|
||||||
their_sum, our_sum)
|
|
||||||
else:
|
|
||||||
_checksum = bytearray([data[-1]])
|
|
||||||
their_sum = _checksum[0]
|
|
||||||
data = data[:-1]
|
|
||||||
|
|
||||||
our_sum = self.calc_checksum(data)
|
|
||||||
valid = their_sum == our_sum
|
|
||||||
if not valid:
|
|
||||||
self.log.warn('recv error: checksum fail '
|
|
||||||
'(theirs=%02x, ours=%02x)',
|
|
||||||
their_sum, our_sum)
|
|
||||||
return valid, data
|
|
||||||
|
|
||||||
def calc_checksum(self, data, checksum=0):
|
|
||||||
'''
|
|
||||||
Calculate the checksum for a given block of data, can also be used to
|
|
||||||
update a checksum.
|
|
||||||
|
|
||||||
>>> csum = modem.calc_checksum('hello')
|
|
||||||
>>> csum = modem.calc_checksum('world', csum)
|
|
||||||
>>> hex(csum)
|
|
||||||
'0x3c'
|
|
||||||
|
|
||||||
'''
|
|
||||||
if platform.python_version_tuple() >= ('3', '0', '0'):
|
|
||||||
return (sum(data) + checksum) % 256
|
|
||||||
else:
|
|
||||||
return (sum(map(ord, data)) + checksum) % 256
|
|
||||||
|
|
||||||
def calc_crc(self, data, crc=0):
|
def calc_crc(self, data, crc=0):
|
||||||
'''
|
'''
|
||||||
Calculate the Cyclic Redundancy Check for a given block of data, can
|
Calculate the Cyclic Redundancy Check for a given block of data, can
|
||||||
|
|
@ -675,6 +373,4 @@ class XMODEM(object):
|
||||||
return crc & 0xffff
|
return crc & 0xffff
|
||||||
|
|
||||||
|
|
||||||
XMODEM1k = partial(XMODEM, mode='xmodem1k')
|
|
||||||
YMODEM = partial(XMODEM, mode='ymodem')
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue