From 9d3a3f3f306af5f1fbd39e537e10392f03f5b27d Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Sat, 30 Jan 2021 00:01:23 -0500 Subject: [PATCH] serialhdl: Update callers to decide serial fd type Don't try to detect a "real serial port" in serialhdl.py. Instead, have the callers invoke either connect_uart(), connect_file(), or connect_pipe(). Signed-off-by: Kevin O'Connor --- klippy/console.py | 14 +++-- klippy/mcu.py | 34 +++++------ klippy/serialhdl.py | 105 ++++++++++++++++++--------------- scripts/spi_flash/spi_flash.py | 6 +- 4 files changed, 89 insertions(+), 70 deletions(-) diff --git a/klippy/console.py b/klippy/console.py index b787111f..ab501d9a 100755 --- a/klippy/console.py +++ b/klippy/console.py @@ -30,8 +30,10 @@ help_txt = """ re_eval = re.compile(r'\{(?P[^}]*)\}') class KeyboardReader: - def __init__(self, ser, reactor): - self.ser = ser + def __init__(self, reactor, serialport, baud): + self.serialport = serialport + self.baud = baud + self.ser = serialhdl.SerialReader(reactor) self.reactor = reactor self.start_time = reactor.monotonic() self.clocksync = clocksync.ClockSync(self.reactor) @@ -52,7 +54,10 @@ class KeyboardReader: def connect(self, eventtime): self.output(help_txt) self.output("="*20 + " attempting to connect " + "="*20) - self.ser.connect() + if self.baud: + self.ser.connect_uart(self.serialport, self.baud) + else: + self.ser.connect_pipe(self.serialport) msgparser = self.ser.get_msgparser() message_count = len(msgparser.get_messages()) version, build_versions = msgparser.get_version_info() @@ -205,8 +210,7 @@ def main(): logging.basicConfig(level=logging.DEBUG) r = reactor.Reactor() - ser = serialhdl.SerialReader(r, serialport, baud) - kbd = KeyboardReader(ser, r) + kbd = KeyboardReader(r, serialport, baud) try: r.run() except KeyboardInterrupt: diff --git a/klippy/mcu.py b/klippy/mcu.py index fe540962..7c6529a2 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -410,24 +410,18 @@ class MCU: self._name = self._name[4:] # Serial port self._serialport = config.get('serial') - serial_rts = True - if config.get('restart_method', None) == "cheetah": - # Special case: Cheetah boards require RTS to be deasserted, else - # a reset will trigger the built-in bootloader. - serial_rts = False - baud = 0 + self._baud = 0 if not (self._serialport.startswith("/dev/rpmsg_") or self._serialport.startswith("/tmp/klipper_host_")): - baud = config.getint('baud', 250000, minval=2400) - self._serial = serialhdl.SerialReader( - self._reactor, self._serialport, baud, serial_rts) + self._baud = config.getint('baud', 250000, minval=2400) + self._serial = serialhdl.SerialReader(self._reactor) # Restarts + restart_methods = [None, 'arduino', 'cheetah', 'command', 'rpi_usb'] self._restart_method = 'command' - if baud: - rmethods = {m: m for m in [None, 'arduino', 'cheetah', 'command', - 'rpi_usb']} - self._restart_method = config.getchoice( - 'restart_method', rmethods, None) + if self._baud: + rmethods = {m: m for m in restart_methods} + self._restart_method = config.getchoice('restart_method', + rmethods, None) self._reset_cmd = self._config_reset_cmd = None self._emergency_stop_cmd = None self._is_shutdown = self._is_timeout = False @@ -612,12 +606,18 @@ class MCU: if self.is_fileoutput(): self._connect_file() else: - if (self._restart_method == 'rpi_usb' - and not os.path.exists(self._serialport)): + resmeth = self._restart_method + if resmeth == 'rpi_usb' and not os.path.exists(self._serialport): # Try toggling usb power self._check_restart("enable power") try: - self._serial.connect() + if self._baud: + # Cheetah boards require RTS to be deasserted + # else a reset will trigger the built-in bootloader. + rts = (resmeth != "cheetah") + self._serial.connect_uart(self._serialport, self._baud, rts) + else: + self._serial.connect_pipe(self._serialport) self._clocksync.connect(self._serial) except serialhdl.error as e: raise error(str(e)) diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index 9ef39dfd..c9eccb50 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -1,6 +1,6 @@ # Serial port management for firmware communication # -# Copyright (C) 2016-2020 Kevin O'Connor +# Copyright (C) 2016-2021 Kevin O'Connor # # This file may be distributed under the terms of the GNU GPLv3 license. import logging, threading, os @@ -13,13 +13,10 @@ class error(Exception): class SerialReader: BITS_PER_BYTE = 10. - def __init__(self, reactor, serialport, baud, rts=True): + def __init__(self, reactor): self.reactor = reactor - self.serialport = serialport - self.baud = baud # Serial port - self.ser = None - self.rts = rts + self.serial_dev = None self.msgparser = msgproto.MessageParser() # C interface self.ffi_main, self.ffi_lib = chelper.get_ffi() @@ -75,42 +72,20 @@ class SerialReader: # Done return identify_data identify_data += msgdata - def connect(self): - # Initial connection - logging.info("Starting serial connect") - start_time = self.reactor.monotonic() - while 1: - connect_time = self.reactor.monotonic() - if connect_time > start_time + 90.: - raise error("Unable to connect") - try: - if self.baud: - self.ser = serial.Serial( - baudrate=self.baud, timeout=0, exclusive=True) - self.ser.port = self.serialport - self.ser.rts = self.rts - self.ser.open() - else: - fd = os.open(self.serialport, os.O_RDWR | os.O_NOCTTY) - self.ser = os.fdopen(fd, 'rb+', 0) - except (OSError, IOError, serial.SerialException) as e: - logging.warn("Unable to open port: %s", e) - self.reactor.pause(connect_time + 5.) - continue - if self.baud: - stk500v2_leave(self.ser, self.reactor) - self.serialqueue = self.ffi_main.gc( - self.ffi_lib.serialqueue_alloc(self.ser.fileno(), 0), - self.ffi_lib.serialqueue_free) - self.background_thread = threading.Thread(target=self._bg_thread) - self.background_thread.start() - # Obtain and load the data dictionary from the firmware - completion = self.reactor.register_callback(self._get_identify_data) - identify_data = completion.wait(connect_time + 5.) - if identify_data is not None: - break - logging.info("Timeout on serial connect") + def _start_session(self, serial_dev): + self.serial_dev = serial_dev + self.serialqueue = self.ffi_main.gc( + self.ffi_lib.serialqueue_alloc(serial_dev.fileno(), 0), + self.ffi_lib.serialqueue_free) + self.background_thread = threading.Thread(target=self._bg_thread) + self.background_thread.start() + # Obtain and load the data dictionary from the firmware + completion = self.reactor.register_callback(self._get_identify_data) + identify_data = completion.wait(self.reactor.monotonic() + 5.) + if identify_data is None: + logging.info("Timeout on connect") self.disconnect() + return False msgparser = msgproto.MessageParser() msgparser.process_identify(identify_data) self.msgparser = msgparser @@ -125,11 +100,49 @@ class SerialReader: if receive_window is not None: self.ffi_lib.serialqueue_set_receive_window( self.serialqueue, receive_window) + return True + def connect_pipe(self, filename): + logging.info("Starting connect") + start_time = self.reactor.monotonic() + while 1: + if self.reactor.monotonic() > start_time + 90.: + raise error("Unable to connect") + try: + fd = os.open(filename, os.O_RDWR | os.O_NOCTTY) + except OSError as e: + logging.warn("Unable to open port: %s", e) + self.reactor.pause(self.reactor.monotonic() + 5.) + continue + serial_dev = os.fdopen(fd, 'rb+', 0) + ret = self._start_session(serial_dev) + if ret: + break + def connect_uart(self, serialport, baud, rts=True): + # Initial connection + logging.info("Starting serial connect") + start_time = self.reactor.monotonic() + while 1: + if self.reactor.monotonic() > start_time + 90.: + raise error("Unable to connect") + try: + serial_dev = serial.Serial(baudrate=baud, timeout=0, + exclusive=True) + serial_dev.port = serialport + serial_dev.rts = rts + serial_dev.open() + except (OSError, IOError, serial.SerialException) as e: + logging.warn("Unable to open serial port: %s", e) + self.reactor.pause(self.reactor.monotonic() + 5.) + continue + stk500v2_leave(serial_dev, self.reactor) + ret = self._start_session(serial_dev) + if ret: + break def connect_file(self, debugoutput, dictionary, pace=False): - self.ser = debugoutput + self.serial_dev = debugoutput self.msgparser.process_identify(dictionary, decompress=False) self.serialqueue = self.ffi_main.gc( - self.ffi_lib.serialqueue_alloc(self.ser.fileno(), 1), + self.ffi_lib.serialqueue_alloc(self.serial_dev.fileno(), 1), self.ffi_lib.serialqueue_free) def set_clock_est(self, freq, last_time, last_clock): self.ffi_lib.serialqueue_set_clock_est( @@ -140,9 +153,9 @@ class SerialReader: if self.background_thread is not None: self.background_thread.join() self.background_thread = self.serialqueue = None - if self.ser is not None: - self.ser.close() - self.ser = None + if self.serial_dev is not None: + self.serial_dev.close() + self.serial_dev = None for pn in self.pending_notifications.values(): pn.complete(None) self.pending_notifications.clear() diff --git a/scripts/spi_flash/spi_flash.py b/scripts/spi_flash/spi_flash.py index 5376dc2d..137adf5a 100644 --- a/scripts/spi_flash/spi_flash.py +++ b/scripts/spi_flash/spi_flash.py @@ -777,9 +777,11 @@ class SDCardSPI: class MCUConnection: def __init__(self, k_reactor, device, baud, board_cfg): self.reactor = k_reactor + self.serial_device = device + self.baud = baud # TODO: a change in baudrate will cause an issue, come up # with a method for handling it gracefully - self._serial = serialhdl.SerialReader(self.reactor, device, baud) + self._serial = serialhdl.SerialReader(self.reactor) self.clocksync = clocksync.ClockSync(self.reactor) self.board_config = board_cfg self.fatfs = None @@ -817,7 +819,7 @@ class MCUConnection: endtime = eventtime + 60. while True: try: - self._serial.connect() + self._serial.connect_uart(self.serial_device, self.baud) self.clocksync.connect(self._serial) except Exception: curtime = self.reactor.monotonic()