From: Jan Luebbe <jlu@xxxxxxxxxxxxxx> 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@xxxxxxxxxxxxxx> Signed-off-by: Sascha Hauer <s.hauer@xxxxxxxxxxxxxx> --- 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 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox