[PATCH 4/4] host side for barebox remote control

Sascha Hauer s.hauer at pengutronix.de
Wed Jun 10 23:54:10 PDT 2015


From: Jan Luebbe <jlu at pengutronix.de>

This contains the host tool for barebox remote control. It is written in
Phython with its own implementation of the RATP protocol. Currently this
is a very simple tool which needs more work, but the code can also be
used as a library.

Example output:

console: '.      '
console: '..     '
console: 'dev    '
console: 'env    '
console: 'mnt    '
console: '\n'
Result: BBPacketCommandReturn(exit_code=0)

Signed-off-by: Jan Lübbe <j.luebbe at pengutronix.de>
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 scripts/bbremote           |   3 +
 scripts/remote/__init__.py |   0
 scripts/remote/main.py     |  57 ++++
 scripts/remote/messages.py | 182 +++++++++++
 scripts/remote/missing.py  |  28 ++
 scripts/remote/ratp.py     | 747 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 1017 insertions(+)
 create mode 100755 scripts/bbremote
 create mode 100644 scripts/remote/__init__.py
 create mode 100644 scripts/remote/main.py
 create mode 100644 scripts/remote/messages.py
 create mode 100644 scripts/remote/missing.py
 create mode 100644 scripts/remote/ratp.py

diff --git a/scripts/bbremote b/scripts/bbremote
new file mode 100755
index 0000000..bc5351d
--- /dev/null
+++ b/scripts/bbremote
@@ -0,0 +1,3 @@
+#!/usr/bin/env python2
+
+import remote.main
diff --git a/scripts/remote/__init__.py b/scripts/remote/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/remote/main.py b/scripts/remote/main.py
new file mode 100644
index 0000000..b7059a5
--- /dev/null
+++ b/scripts/remote/main.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python2
+
+from __future__ import absolute_import, division, print_function
+
+
+import argparse
+import logging
+import serial
+
+from .ratp import SerialRatpConnection
+from .messages import Controller
+
+
+def handle_run(args):
+    port = serial.serial_for_url(args.port, args.baudrate)
+    conn = SerialRatpConnection(port)
+    ctrl = Controller(conn)
+    res = ctrl.command(' '.join(args.arg))
+    print("Result:", res)
+    ctrl.close()
+
+
+def handle_listen(args):
+    port = serial.serial_for_url(args.port, args.baudrate)
+    conn = SerialRatpConnection(port)
+    conn.listen()
+    while True:
+        conn.wait(None)
+    conn.close()
+
+
+def handle_test(args):
+    pass
+
+VERBOSITY = {
+    0: logging.WARN,
+    1: logging.INFO,
+    2: logging.DEBUG,
+    }
+
+parser = argparse.ArgumentParser(prog='bbremote')
+parser.add_argument('-v', '--verbose', action='count', default=0)
+parser.add_argument('--port', type=str)
+parser.add_argument('--baudrate', type=int, default=115200)
+subparsers = parser.add_subparsers(help='sub-command help')
+
+parser_run = subparsers.add_parser('run', help="run a barebox command")
+parser_run.add_argument('arg', nargs='+', help="barebox command to run")
+parser_run.set_defaults(func=handle_run)
+
+parser_run = subparsers.add_parser('listen', help="run a barebox command")
+parser_run.set_defaults(func=handle_listen)
+
+args = parser.parse_args()
+logging.basicConfig(level=VERBOSITY[args.verbose],
+                    format='%(levelname)-8s %(module)-8s %(funcName)-16s %(message)s')
+args.func(args)
diff --git a/scripts/remote/messages.py b/scripts/remote/messages.py
new file mode 100644
index 0000000..5795b78
--- /dev/null
+++ b/scripts/remote/messages.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, print_function
+
+import struct
+import logging
+
+try:
+    from time import monotonic
+except:
+    from .missing import monotonic
+
+
+class BBType(object):
+    command = 1
+    command_return = 2
+    consolemsg = 3
+    ping = 4
+    pong = 5
+
+
+class BBPacket(object):
+    def __init__(self, p_type=0, p_flags=0, raw=None):
+        self.p_type = p_type
+        self.p_flags = p_flags
+        if raw is not None:
+            self.unpack(raw)
+
+    def __repr__(self):
+        return "BBPacket(%i, %i)" % (self.p_type, self.p_flags)
+
+    def _unpack_payload(self, data):
+        pass
+
+    def _pack_payload(self):
+        return ""
+
+    def unpack(self, data):
+        self.p_type, self.p_flags = struct.unpack("!HH", data[:4])
+        self._unpack_payload(data[4:])
+
+    def pack(self):
+        return struct.pack("!HH", self.p_type, self.p_flags) + \
+            self._pack_payload()
+
+
+class BBPacketCommand(BBPacket):
+    def __init__(self, raw=None, cmd=None):
+        self.cmd = cmd
+        super(BBPacketCommand, self).__init__(BBType.command, raw=raw)
+
+    def __repr__(self):
+        return "BBPacketCommand(cmd=%r)" % self.cmd
+
+    def _unpack_payload(self, payload):
+        self.cmd = payload
+
+    def _pack_payload(self):
+        return self.cmd
+
+
+class BBPacketCommandReturn(BBPacket):
+    def __init__(self, raw=None, exit_code=None):
+        self.exit_code = exit_code
+        super(BBPacketCommandReturn, self).__init__(BBType.command_return,
+                                                    raw=raw)
+
+    def __repr__(self):
+        return "BBPacketCommandReturn(exit_code=%i)" % self.exit_code
+
+    def _unpack_payload(self, data):
+        self.exit_code = struct.unpack("!L", data[:4])
+
+    def _pack_payload(self):
+        return struct.pack("!L", self.exit_code)
+
+
+class BBPacketConsoleMsg(BBPacket):
+    def __init__(self, raw=None, text=None):
+        self.text = text
+        super(BBPacketConsoleMsg, self).__init__(BBType.consolemsg, raw=raw)
+
+    def __repr__(self):
+        return "BBPacketConsoleMsg(text=%r)" % self.text
+
+    def _unpack_payload(self, payload):
+        self.text = payload
+
+    def _pack_payload(self):
+        return self.text
+
+
+class BBPacketPing(BBPacket):
+    def __init__(self, raw=None):
+        super(BBPacketPing, self).__init__(BBType.ping, raw=raw)
+
+    def __repr__(self):
+        return "BBPacketPing()"
+
+
+class BBPacketPong(BBPacket):
+    def __init__(self, raw=None):
+        super(BBPacketPong, self).__init__(BBType.pong, raw=raw)
+
+    def __repr__(self):
+        return "BBPacketPong()"
+
+
+def unpack(data):
+    p_type, = struct.unpack("!H", data[:2])
+    logging.debug("unpack: %r data=%r", p_type, repr(data))
+    if p_type == BBType.command:
+        logging.debug("received: command")
+        return BBPacketCommand(raw=data)
+    elif p_type == BBType.command_return:
+        logging.debug("received: command_return")
+        return BBPacketCommandReturn(raw=data)
+    elif p_type == BBType.consolemsg:
+        logging.debug("received: consolemsg")
+        return BBPacketConsoleMsg(raw=data)
+    elif p_type == BBType.ping:
+        logging.debug("received: ping")
+        return BBPacketPing(raw=data)
+    elif p_type == BBType.pong:
+        logging.debug("received: pong")
+        return BBPacketPong(raw=data)
+    else:
+        logging.debug("received: UNKNOWN")
+        return BBPacket(raw=data)
+
+
+class Controller(object):
+    def __init__(self, conn):
+        self.conn = conn
+        self.conn.connect()
+
+    def _send(self, bbpkt):
+        self.conn.send(bbpkt.pack())
+
+    def _handle(self, bbpkt):
+        if isinstance(bbpkt, BBPacketConsoleMsg):
+            print("console:", repr(bbpkt.text))
+
+    def _expect(self, bbtype, timeout=1.0):
+        limit = monotonic()+timeout
+        while limit > monotonic():
+            pkt = self.conn.recv(timeout)
+            if not pkt:
+                continue
+            bbpkt = unpack(pkt)
+            if isinstance(bbpkt, bbtype):
+                return bbpkt
+            else:
+                self._handle(bbpkt)
+
+    def ping(self):
+        self._send(BBPacketPing())
+        r = self._expect(BBPacketPong)
+        logging.info("Ping: %r", r)
+
+    def command(self, cmd):
+        self._send(BBPacketCommand(cmd=cmd))
+        r = self._expect(BBPacketCommandReturn)
+        logging.info("Command: %r", r)
+        return r
+
+    def close(self):
+        self.conn.close()
+
+
+def main():
+    import serial
+    from .ratp import SerialRatpConnection
+    url = "rfc2217://192.168.23.176:3002"
+    port = serial.serial_for_url(url, 115200)
+    conn = SerialRatpConnection(port)
+    ctrl = Controller(conn)
+    return ctrl
+
+if __name__ == "__main__":
+    C = main()
diff --git a/scripts/remote/missing.py b/scripts/remote/missing.py
new file mode 100644
index 0000000..67c2dfa
--- /dev/null
+++ b/scripts/remote/missing.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+import ctypes
+import os
+
+CLOCK_MONOTONIC_RAW = 4  # from <linux/time.h>
+
+
+class timespec(ctypes.Structure):
+    _fields_ = [
+        ('tv_sec', ctypes.c_long),
+        ('tv_nsec', ctypes.c_long)
+    ]
+
+librt = ctypes.CDLL('librt.so.1', use_errno=True)
+clock_gettime = librt.clock_gettime
+clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
+
+
+def monotonic():
+    t = timespec()
+    if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(t)) != 0:
+        errno_ = ctypes.get_errno()
+        raise OSError(errno_, os.strerror(errno_))
+    return t.tv_sec + t.tv_nsec * 1e-9
+
+if __name__ == "__main__":
+    print monotonic()
diff --git a/scripts/remote/ratp.py b/scripts/remote/ratp.py
new file mode 100644
index 0000000..f05bc9f
--- /dev/null
+++ b/scripts/remote/ratp.py
@@ -0,0 +1,747 @@
+#!/usr/bin/env python2
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import, division, print_function
+
+import crcmod
+import logging
+import struct
+from enum import Enum
+from time import sleep
+
+try:
+    from time import monotonic
+except:
+    from .missing import monotonic
+
+csum_func = crcmod.predefined.mkCrcFun('xmodem')
+
+
+class RatpState(Enum):
+    listen = "listen"  # 1
+    syn_sent = "syn-sent"  # 2
+    syn_received = "syn-received"  # 3
+    established = "established"  # 4
+    fin_wait = "fin-wait"  # 5
+    last_ack = "last-ack"  # 6
+    closing = "closing"  # 7
+    time_wait = "time-wait"  # 8
+    closed = "closed"  # 9
+
+
+class RatpInvalidHeader(ValueError):
+    pass
+
+
+class RatpInvalidPayload(ValueError):
+    pass
+
+
+class RatpError(ValueError):
+    pass
+
+
+class RaptPacket(object):
+
+    def __init__(self, data=None, flags=''):
+        self.payload = None
+        self.synch = 0x01
+        self._control = 0
+        self.length = 0
+        self.csum = 0
+        self.c_syn = False
+        self.c_ack = False
+        self.c_fin = False
+        self.c_rst = False
+        self.c_sn = 0
+        self.c_an = 0
+        self.c_eor = False
+        self.c_so = False
+        if data:
+            (self.synch, self._control, self.length, self.csum) = \
+                struct.unpack('!BBBB', data)
+            if self.synch != 0x01:
+                raise RatpInvalidHeader("invalid synch octet (%x != %x)" %
+                                        (self.synch, 0x01))
+            csum = (self._control + self.length + self.csum) & 0xff
+            if csum != 0xff:
+                raise RatpInvalidHeader("invalid csum octet (%x != %x)" %
+                                        (csum, 0xff))
+            self._unpack_control()
+        elif flags:
+            if 'S' in flags:
+                self.c_syn = True
+            if 'A' in flags:
+                self.c_ack = True
+            if 'F' in flags:
+                self.c_fin = True
+            if 'R' in flags:
+                self.c_rst = True
+
+    def __repr__(self):
+        s = "RaptPacket("
+        if self.c_syn:
+            s += "SYN,"
+        if self.c_ack:
+            s += "ACK,"
+        if self.c_fin:
+            s += "FIN,"
+        if self.c_rst:
+            s += "RST,"
+        s += "SN=%i,AN=%i," % (self.c_sn, self.c_an)
+        if self.c_eor:
+            s += "EOR,"
+        if self.c_so:
+            s += "SO,DATA=%i)" % self.length
+        else:
+            s += "DATA=%i)" % self.length
+        return s
+
+    def _pack_control(self):
+        self._control = 0 | \
+            self.c_syn << 7 | \
+            self.c_ack << 6 | \
+            self.c_fin << 5 | \
+            self.c_rst << 4 | \
+            self.c_sn << 3 | \
+            self.c_an << 2 | \
+            self.c_eor << 1 | \
+            self.c_so << 0
+
+    def _unpack_control(self):
+        self.c_syn = bool(self._control & 1 << 7)
+        self.c_ack = bool(self._control & 1 << 6)
+        self.c_fin = bool(self._control & 1 << 5)
+        self.c_rst = bool(self._control & 1 << 4)
+        self.c_sn = bool(self._control & 1 << 3)
+        self.c_an = bool(self._control & 1 << 2)
+        self.c_eor = bool(self._control & 1 << 1)
+        self.c_so = bool(self._control & 1 << 0)
+
+    def pack(self):
+        self._pack_control()
+        self.csum = 0
+        self.csum = (self._control + self.length + self.csum)
+        self.csum = (self.csum & 0xff) ^ 0xff
+        return struct.pack('!BBBB', self.synch, self._control, self.length,
+                           self.csum)
+
+    def unpack_payload(self, payload):
+        (c_recv,) = struct.unpack('!H', payload[-2:])
+        c_calc = csum_func(payload[:-2])
+        if c_recv != c_calc:
+            raise RatpInvalidPayload("bad checksum (%04x != %04x)" %
+                                     (c_recv, c_calc))
+        self.payload = payload[:-2]
+
+    def pack_payload(self):
+        c_calc = csum_func(self.payload)
+        return self.payload+struct.pack('!H', c_calc)
+
+
+class RatpConnection(object):
+    def __init__(self):
+        self._state = RatpState.closed
+        self._passive = True
+        self._input = b''
+        self._s_sn = 0
+        self._s_an = 0
+        self._r_sn = 0
+        self._r_an = 0
+        self._retrans = None
+        self._retrans_counter = None
+        self._retrans_deadline = None
+        self._r_mdl = None
+        self._s_mdl = 0xff
+        self._rx_queue = []
+        self._tx_queue = []
+        self._rtt_alpha = 0.8
+        self._rtt_beta = 2.0
+        self._srtt = 0.2
+        self._rto_min, self._rto_max = 0.01, 1
+        self._tx_timestamp = None
+
+    def _update_srtt(self, rtt):
+        self._srtt = (self._rtt_alpha * self._srtt) + \
+                     ((1.0 - self._rtt_alpha) * rtt)
+        logging.info("SRTT: %r", self._srtt)
+
+    def _get_rto(self):
+        return min(self._rto_max,
+                   max(self._rto_min, self._rtt_beta * self._srtt))
+
+    def _write(self, pkt):
+        if self._retrans not in (None, pkt):
+            raise RatpError("%r not in %r" % (self._retrans, (None, pkt)))
+
+        if pkt.payload or pkt.c_so or pkt.c_syn or pkt.c_rst or pkt.c_fin:
+            # FIXME retransmit other packets?
+            if not self._retrans:
+                self._retrans = pkt
+                self._retrans_counter = 0
+            else:
+                self._retrans_counter += 1
+                if self._retrans_counter > 10:
+                    raise RatpError("Maximum retransmit count exceeded")
+            self._retrans_deadline = monotonic()+self._get_rto()
+
+        self._s_sn = pkt.c_sn
+        if pkt.c_ack:
+            self._s_an = pkt.c_an
+
+        logging.info("Write: %r", pkt)
+
+        self._write_raw(pkt.pack())
+        if pkt.payload:
+            self._write_raw(pkt.pack_payload())
+        self._tx_timestamp = monotonic()
+
+    def _check_rto(self):
+        if self._retrans is None:
+            return
+
+        if self._retrans_deadline < monotonic():
+            logging.debug("Retransmit...")
+            self._write(self._retrans)
+
+    def _check_time_wait(self):
+        if not self._state == RatpState.time_wait:
+            return
+
+        remaining = self._time_wait_deadline - monotonic()
+        if remaining < 0:
+            self._state = RatpState.closed
+        else:
+            logging.debug("Time-Wait: %.2f remaining" % remaining)
+            sleep(min(remaining, 0.1))
+
+    def _read(self):
+        if len(self._input) < 4:
+            self._input += self._read_raw(4-len(self._input))
+        if len(self._input) < 4:
+            return
+
+        try:
+            pkt = RaptPacket(data=self._input[:4])
+        except RatpInvalidHeader as e:
+            logging.info("%r", e)
+            self._input = self._input[1:]
+            return
+
+        self._input = self._input[4:]
+
+        logging.info("Read: %r", pkt)
+
+        if pkt.c_syn or pkt.c_rst or pkt.c_so or pkt.c_fin:
+            return pkt
+
+        if pkt.length == 0:
+            return pkt
+
+        while len(self._input) < pkt.length+2:
+            self._input += self._read_raw()
+
+        try:
+            pkt.unpack_payload(self._input[:pkt.length+2])
+        except RatpInvalidPayload as e:
+            return
+        finally:
+            self._input = self._input[pkt.length+2:]
+
+        return pkt
+
+    def _close(self):
+        pass
+
+    def _a(self, r):
+        logging.info("A")
+
+        if r.c_rst:
+            return True
+
+        if r.c_ack:
+            s = RaptPacket(flags='R')
+            s.c_sn = r.c_an
+            self._write(s)
+            return False
+
+        if r.c_syn:
+            self._r_mdl = r.length
+            #self._r_sn = r.c_sn  # already store in the state machine
+
+            s = RaptPacket(flags='SA')
+            s.c_sn = 0
+            s.c_an = (r.c_sn + 1) % 2
+            s.length = self._s_mdl
+            self._write(s)
+            self._state = RatpState.syn_received
+            return False
+
+        return False
+
+    def _b(self, r):
+        logging.info("B")
+
+        if r.c_ack and r.c_an != (self._s_sn + 1) % 2:
+            if r.c_rst:
+                return False
+            else:
+                s = RaptPacket(flags='R')
+                s.c_sn = r.c_an
+                self._write(s)
+                return False
+
+        if r.c_rst:
+            if r.c_ack:
+                self._retrans = None
+                # FIXME: delete the TCB
+                self._state = RatpState.closed
+                return False
+            else:
+                return False
+
+        if r.c_syn:
+            if r.c_ack:
+                self._r_mdl = r.length
+                self._retrans = None
+                s = RaptPacket(flags='A')
+                s.c_sn = r.c_an
+                s.c_an = (r.c_sn + 1) % 2
+                self._write(s)
+                self._state = RatpState.established
+                return False
+            else:
+                self._retrans = None
+                s = RaptPacket(flags='SA')
+                s.c_sn = 0
+                s.c_an = (r.c_sn + 1) % 2
+                s.length = self._s_mdl
+                self._write(s)
+                self._state = RatpState.syn_received
+                return False
+
+        return False
+
+    def _c1(self, r):
+        logging.info("C1")
+
+        if r.c_sn == self._s_an:
+            return True
+
+        if r.c_rst or r.c_fin:
+            return False
+
+        s = RaptPacket(flags='A')
+        s.c_sn = r.c_an
+        s.c_an = (r.c_sn + 1) % 2
+        self._write(s)
+        return False
+
+    def _c2(self, r):
+        logging.info("C2")
+
+        if r.c_sn == self._s_an:
+            return True
+
+        if r.c_rst or r.c_fin:
+            return False
+
+        if r.c_syn:
+            s = RaptPacket(flags='RA')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+            self._retrans = None
+            # FIXME: inform the user "Error: Connection reset"
+            self._state = RatpState.closed
+            return False
+
+        # FIXME: only ack duplicate data packages?
+        # This is not documented in RFC 916
+        if r.length or r.c_so:
+            logging.info("C2: duplicate data packet, dropping")
+            s = RaptPacket(flags='A')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+
+        return False
+
+    def _d1(self, r):
+        logging.info("D1")
+
+        if not r.c_rst:
+            return True
+
+        if self._passive:
+            self._retrans = None
+            self._state = RatpState.listen
+            return False
+        else:
+            self._retrans = None
+            # FIXME: inform the user "Error: Connection refused"
+            self._state = RatpState.closed
+
+    def _d2(self, r):
+        logging.info("D2")
+
+        if not r.c_rst:
+            return True
+
+        self._retrans = None
+        # FIXME: inform the user "Error: Connection reset"
+        self._state = RatpState.closed
+        return False
+
+    def _d3(self, r):
+        logging.info("C3")
+
+        if not r.c_rst:
+            return True
+
+        self._state = RatpState.closed
+        return False
+
+    def _e(self, r):
+        logging.info("E")
+
+        if not r.c_syn:
+            return True
+
+        self._retrans = None
+        s = RaptPacket(flags='R')
+        if r.c_ack:
+            s.c_sn = r.c_an
+        else:
+            s.c_sn = 0
+        self._write(s)
+        # FIXME: inform the user "Error: Connection reset"
+        self._state = RatpState.closed
+        return False
+
+    def _f1(self, r):
+        logging.info("F1")
+        
+        if not r.c_ack:
+            return False
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            return True
+
+        if self._passive:
+            self._retrans = None
+            s = RaptPacket(flags='R')
+            s.c_sn = r.c_an
+            self._write(s)
+            self._state = RatpState.listen
+            return False
+        else:
+            # FIXME: inform the user "Error: Connection refused"
+            self._retrans = None
+            s = RaptPacket(flags='R')
+            s.c_sn = r.c_an
+            self._write(s)
+            self._state = RatpState.closed
+            return False
+
+    def _f2(self, r):
+        logging.info("F2")
+
+        if not r.c_ack:
+            return False
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            if self._retrans:
+                self._retrans = None
+                self._update_srtt(monotonic()-self._tx_timestamp)
+                # FIXME: inform the user with an "Ok" if a buffer has been
+                # entirely acknowledged.  Another packet containing data may
+                # now be sent.
+            return True
+
+        return True
+
+    def _f3(self, r):
+        logging.info("F3")
+
+        if not r.c_ack:
+            return False
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            return True
+
+        return True
+
+    def _g(self, r):
+        logging.info("G")
+
+        if not r.c_rst:
+            return False
+
+        self._retrans = None
+        if r.c_ack:
+            s = RaptPacket(flags='R')
+            s.c_sn = r.c_an
+            self._write(s)
+        else:
+            s = RaptPacket(flags='RA')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+
+        return False
+
+    def _h1(self, r):
+        logging.info("H1")
+
+        # FIXME: initial data?
+        self._state = RatpState.established
+
+        return False
+
+    def _h2(self, r):
+        logging.info("H2")
+
+        if not r.c_fin:
+            return True
+
+        if self._retrans is not None:
+            # FIXME: inform the user "Warning: Data left unsent.", "Connection closing."
+            self._retrans = None
+        s = RaptPacket(flags='FA')
+        s.c_sn = r.c_an
+        s.c_an = (r.c_sn + 1) % 2
+        self._write(s)
+        self._state = RatpState.last_ack
+        return False
+
+    def _h3(self, r):
+        logging.info("H3")
+
+        if not r.c_fin:
+            # Our fin was lost, rely on retransmission
+            return False
+
+        if r.length or r.c_so:
+            self._retrans = None
+            s = RaptPacket(flags='RA')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+            # FIXME: inform the user "Error: Connection reset."
+            self._state = RatpState.closed
+            return False
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            self._retrans = None
+            s = RaptPacket(flags='A')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+            self._time_wait_deadline = monotonic() + self._get_rto()
+            self._state = RatpState.time_wait
+            return False
+        else:
+            self._retrans = None
+            s = RaptPacket(flags='A')
+            s.c_sn = r.c_an
+            s.c_an = (r.c_sn + 1) % 2
+            self._write(s)
+            self._state = RatpState.closing
+            return False
+
+    def _h4(self, r):
+        logging.info("H4")
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            self._retrans = None
+            self._state = RatpState.time_wait
+            return False
+
+        return False
+
+    def _h5(self, r):
+        logging.info("H5")
+
+        if r.c_an == (self._s_sn + 1) % 2:
+            self._time_wait_deadline = monotonic() + self._get_rto()
+            self._state = RatpState.time_wait
+            return False
+
+        return False
+
+    def _h6(self, r):
+        logging.info("H6")
+
+        if not r.c_ack:
+            return False
+
+        if not r.c_fin:
+            return False
+
+        self._retrans = None
+        s = RaptPacket(flags='A')
+        s.c_sn = r.c_an
+        s.c_an = (r.c_sn + 1) % 2
+        self._write(s)
+        self._time_wait_deadline = monotonic() + self._get_rto()
+        return False
+
+    def _i1(self, r):
+        logging.info("I1")
+
+        if r.c_so:
+            self._rx_queue.append(chr(r.length))
+        elif r.length:
+            self._rx_queue.append(r.payload)
+        else:
+            return False
+
+        self._retrans = None
+        s = RaptPacket(flags='A')
+        s.c_sn = r.c_an
+        s.c_an = (r.c_sn + 1) % 2
+        self._write(s)
+        return False
+
+    def _machine(self, pkt):
+        self._r_sn = pkt.c_sn
+        if pkt.c_ack:
+            self._r_an = pkt.c_an
+        logging.info("State: %r", self._state)
+        if self._state == RatpState.listen:
+            self._a(pkt)
+        elif self._state == RatpState.syn_sent:
+            self._b(pkt)
+        elif self._state == RatpState.syn_received:
+            self._c1(pkt) and \
+                self._d1(pkt) and \
+                self._e(pkt) and \
+                self._f1(pkt) and \
+                self._h1(pkt)
+        elif self._state == RatpState.established:
+            self._c2(pkt) and \
+                self._d2(pkt) and \
+                self._e(pkt) and \
+                self._f2(pkt) and \
+                self._h2(pkt) and \
+                self._i1(pkt)
+        elif self._state == RatpState.fin_wait:
+            self._c2(pkt) and \
+                self._d2(pkt) and \
+                self._e(pkt) and \
+                self._f3(pkt) and \
+                self._h3(pkt)
+        elif self._state == RatpState.last_ack:
+            self._c2(pkt) and \
+                self._d3(pkt) and \
+                self._e(pkt) and \
+                self._f3(pkt) and \
+                self._h4(pkt)
+        elif self._state == RatpState.closing:
+            self._c2(pkt) and \
+                self._d3(pkt) and \
+                self._e(pkt) and \
+                self._f3(pkt) and \
+                self._h5(pkt)
+        elif self._state == RatpState.time_wait:
+            self._d3(pkt) and \
+                self._e(pkt) and \
+                self._f3(pkt) and \
+                self._h6(pkt)
+        elif self._state == RatpState.closed:
+            self._g(pkt)
+
+    def wait(self, deadline):
+        while deadline is None or deadline > monotonic():
+            pkt = self._read()
+            if pkt:
+                self._machine(pkt)
+            else:
+                self._check_rto()
+                self._check_time_wait()
+            if not self._retrans or self._rx_queue:
+                return
+
+    def listen(self):
+        logging.info("LISTEN")
+        self._state = RatpState.listen
+
+    def connect(self, timeout=5.0):
+        deadline = monotonic() + timeout
+        logging.info("CONNECT")
+        self._retrans = None
+        syn = RaptPacket(flags='S')
+        syn.length = self._s_mdl
+        self._write(syn)
+        self._state = RatpState.syn_sent
+        self.wait(deadline)
+
+    def send(self, data, timeout=1.0):
+        deadline = monotonic() + timeout
+        logging.info("SEND")
+        assert self._state == RatpState.established
+        self._retrans = None
+        snd = RaptPacket(flags='A')
+        snd.c_sn = self._r_an
+        snd.c_an = self._s_an
+        snd.length = len(data)
+        snd.payload = data
+        self._write(snd)
+        self.wait(deadline)
+
+    def recv(self, timeout=1.0):
+        deadline = monotonic() + timeout
+        logging.info("RECV")
+        assert self._state == RatpState.established
+        if self._rx_queue:
+            return self._rx_queue.pop(0)
+        self.wait(deadline)
+        if self._rx_queue:
+            return self._rx_queue.pop(0)
+
+    def close(self, timeout=1.0):
+        deadline = monotonic() + timeout
+        logging.info("CLOSE")
+        if self._state == RatpState.established:
+            fin = RaptPacket(flags='FA')  # FIXME: only F?
+            fin.c_sn = self._r_an  # FIXME: my _s_sn?
+            fin.c_an = (self._r_sn + 1) % 2
+            self._write(fin)
+            self._state = RatpState.fin_wait
+        while deadline > monotonic() and not self._state == RatpState.time_wait:
+            self.wait(deadline)
+        while self._state == RatpState.time_wait:
+            self.wait(None)
+        if self._state == RatpState.closed:
+            logging.info("CLOSE: success")
+        else:
+            logging.info("CLOSE: failure")
+
+
+    def abort(self):
+        logging.info("ABORT")
+
+    def status(self):
+        logging.info("STATUS")
+        return self._state
+
+
+class SerialRatpConnection(RatpConnection):
+    def __init__(self, port):
+        super(SerialRatpConnection, self).__init__()
+        self.__port = port
+        self.__port.timeout = 0.1
+        self.__port.writeTimeout = None
+        self.__port.flushInput()
+
+    def _write_raw(self, data):
+        if data:
+            logging.debug("-> %r", bytearray(data))
+        return self.__port.write(data)
+
+    def _read_raw(self, size=1):
+        data = self.__port.read(size)
+        if data:
+            logging.debug("<- %r", bytearray(data))
+        return data
-- 
2.1.4




More information about the barebox mailing list