]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - processor-sdk/performance-audio-sr.git/blobdiff - procsdk_audio_x_xx_xx_xx/tools/pyalpha/pyalpha/serial/rfc2217.py
Change root folder name, create pasdk subfolder
[processor-sdk/performance-audio-sr.git] / procsdk_audio_x_xx_xx_xx / tools / pyalpha / pyalpha / serial / rfc2217.py
diff --git a/procsdk_audio_x_xx_xx_xx/tools/pyalpha/pyalpha/serial/rfc2217.py b/procsdk_audio_x_xx_xx_xx/tools/pyalpha/pyalpha/serial/rfc2217.py
deleted file mode 100644 (file)
index 5e3cbe3..0000000
+++ /dev/null
@@ -1,1341 +0,0 @@
-#! python
-#
-# This module implements a RFC2217 compatible client. RF2217 descibes a
-# protocol to access serial ports over TCP/IP and allows setting the baud rate,
-# modem control lines etc.
-#
-# This file is part of pySerial. https://github.com/pyserial/pyserial
-# (C) 2001-2015 Chris Liechti <cliechti@gmx.net>
-#
-# SPDX-License-Identifier:    BSD-3-Clause
-
-# TODO:
-# - setting control line -> answer is not checked (had problems with one of the
-#   severs). consider implementing a compatibility mode flag to make check
-#   conditional
-# - write timeout not implemented at all
-
-# ###########################################################################
-# observations and issues with servers
-# ===========================================================================
-# sredird V2.2.1
-# - http://www.ibiblio.org/pub/Linux/system/serial/   sredird-2.2.2.tar.gz
-# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding
-#   [105 1] instead of the actual value.
-# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger
-#   numbers than 2**32?
-# - To get the signature [COM_PORT_OPTION 0] has to be sent.
-# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done
-# ===========================================================================
-# telnetcpcd (untested)
-# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz
-# - To get the signature [COM_PORT_OPTION] w/o data has to be sent.
-# ===========================================================================
-# ser2net
-# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least
-#   acknowledges that the client activates these options
-# - The configuration may be that the server prints a banner. As this client
-#   implementation does a flushInput on connect, this banner is hidden from
-#   the user application.
-# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one
-#   second.
-# - To get the signature [COM_PORT_OPTION 0] has to be sent.
-# - run a server: run ser2net daemon, in /etc/ser2net.conf:
-#     2000:telnet:0:/dev/ttyS0:9600 remctl banner
-# ###########################################################################
-
-# How to identify ports? pySerial might want to support other protocols in the
-# future, so lets use an URL scheme.
-# for RFC2217 compliant servers we will use this:
-#    rfc2217://<host>:<port>[?option[&option...]]
-#
-# options:
-# - "logging" set log level print diagnostic messages (e.g. "logging=debug")
-# - "ign_set_control": do not look at the answers to SET_CONTROL
-# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read.
-#   Without this option it expects that the server sends notifications
-#   automatically on change (which most servers do and is according to the
-#   RFC).
-# the order of the options is not relevant
-
-import logging
-import socket
-import struct
-import threading
-import time
-try:
-    import urlparse
-except ImportError:
-    import urllib.parse as urlparse
-try:
-    import Queue
-except ImportError:
-    import queue as Queue
-
-import serial
-from serial.serialutil import SerialBase, SerialException, to_bytes, iterbytes, portNotOpenError
-
-# port string is expected to be something like this:
-# rfc2217://host:port
-# host may be an IP or including domain, whatever.
-# port is 0...65535
-
-# map log level names to constants. used in from_url()
-LOGGER_LEVELS = {
-    'debug': logging.DEBUG,
-    'info': logging.INFO,
-    'warning': logging.WARNING,
-    'error': logging.ERROR,
-}
-
-
-# telnet protocol characters
-SE = b'\xf0'    # Subnegotiation End
-NOP = b'\xf1'   # No Operation
-DM = b'\xf2'    # Data Mark
-BRK = b'\xf3'   # Break
-IP = b'\xf4'    # Interrupt process
-AO = b'\xf5'    # Abort output
-AYT = b'\xf6'   # Are You There
-EC = b'\xf7'    # Erase Character
-EL = b'\xf8'    # Erase Line
-GA = b'\xf9'    # Go Ahead
-SB = b'\xfa'    # Subnegotiation Begin
-WILL = b'\xfb'
-WONT = b'\xfc'
-DO = b'\xfd'
-DONT = b'\xfe'
-IAC = b'\xff'   # Interpret As Command
-IAC_DOUBLED = b'\xff\xff'
-
-# selected telnet options
-BINARY = b'\x00'    # 8-bit data path
-ECHO = b'\x01'      # echo
-SGA = b'\x03'       # suppress go ahead
-
-# RFC2217
-COM_PORT_OPTION = b'\x2c'
-
-# Client to Access Server
-SET_BAUDRATE = b'\x01'
-SET_DATASIZE = b'\x02'
-SET_PARITY = b'\x03'
-SET_STOPSIZE = b'\x04'
-SET_CONTROL = b'\x05'
-NOTIFY_LINESTATE = b'\x06'
-NOTIFY_MODEMSTATE = b'\x07'
-FLOWCONTROL_SUSPEND = b'\x08'
-FLOWCONTROL_RESUME = b'\x09'
-SET_LINESTATE_MASK = b'\x0a'
-SET_MODEMSTATE_MASK = b'\x0b'
-PURGE_DATA = b'\x0c'
-
-SERVER_SET_BAUDRATE = b'\x65'
-SERVER_SET_DATASIZE = b'\x66'
-SERVER_SET_PARITY = b'\x67'
-SERVER_SET_STOPSIZE = b'\x68'
-SERVER_SET_CONTROL = b'\x69'
-SERVER_NOTIFY_LINESTATE = b'\x6a'
-SERVER_NOTIFY_MODEMSTATE = b'\x6b'
-SERVER_FLOWCONTROL_SUSPEND = b'\x6c'
-SERVER_FLOWCONTROL_RESUME = b'\x6d'
-SERVER_SET_LINESTATE_MASK = b'\x6e'
-SERVER_SET_MODEMSTATE_MASK = b'\x6f'
-SERVER_PURGE_DATA = b'\x70'
-
-RFC2217_ANSWER_MAP = {
-    SET_BAUDRATE: SERVER_SET_BAUDRATE,
-    SET_DATASIZE: SERVER_SET_DATASIZE,
-    SET_PARITY: SERVER_SET_PARITY,
-    SET_STOPSIZE: SERVER_SET_STOPSIZE,
-    SET_CONTROL: SERVER_SET_CONTROL,
-    NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE,
-    NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE,
-    FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND,
-    FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME,
-    SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK,
-    SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK,
-    PURGE_DATA: SERVER_PURGE_DATA,
-}
-
-SET_CONTROL_REQ_FLOW_SETTING = b'\x00'        # Request Com Port Flow Control Setting (outbound/both)
-SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01'     # Use No Flow Control (outbound/both)
-SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02'     # Use XON/XOFF Flow Control (outbound/both)
-SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03'     # Use HARDWARE Flow Control (outbound/both)
-SET_CONTROL_REQ_BREAK_STATE = b'\x04'         # Request BREAK State
-SET_CONTROL_BREAK_ON = b'\x05'                # Set BREAK State ON
-SET_CONTROL_BREAK_OFF = b'\x06'               # Set BREAK State OFF
-SET_CONTROL_REQ_DTR = b'\x07'                 # Request DTR Signal State
-SET_CONTROL_DTR_ON = b'\x08'                  # Set DTR Signal State ON
-SET_CONTROL_DTR_OFF = b'\x09'                 # Set DTR Signal State OFF
-SET_CONTROL_REQ_RTS = b'\x0a'                 # Request RTS Signal State
-SET_CONTROL_RTS_ON = b'\x0b'                  # Set RTS Signal State ON
-SET_CONTROL_RTS_OFF = b'\x0c'                 # Set RTS Signal State OFF
-SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d'     # Request Com Port Flow Control Setting (inbound)
-SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e'  # Use No Flow Control (inbound)
-SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f'   # Use XON/XOFF Flow Control (inbound)
-SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10'   # Use HARDWARE Flow Control (inbound)
-SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11'    # Use DCD Flow Control (outbound/both)
-SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12'    # Use DTR Flow Control (inbound)
-SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13'    # Use DSR Flow Control (outbound/both)
-
-LINESTATE_MASK_TIMEOUT = 128        # Time-out Error
-LINESTATE_MASK_SHIFTREG_EMPTY = 64  # Transfer Shift Register Empty
-LINESTATE_MASK_TRANSREG_EMPTY = 32  # Transfer Holding Register Empty
-LINESTATE_MASK_BREAK_DETECT = 16    # Break-detect Error
-LINESTATE_MASK_FRAMING_ERROR = 8    # Framing Error
-LINESTATE_MASK_PARTIY_ERROR = 4     # Parity Error
-LINESTATE_MASK_OVERRUN_ERROR = 2    # Overrun Error
-LINESTATE_MASK_DATA_READY = 1       # Data Ready
-
-MODEMSTATE_MASK_CD = 128            # Receive Line Signal Detect (also known as Carrier Detect)
-MODEMSTATE_MASK_RI = 64             # Ring Indicator
-MODEMSTATE_MASK_DSR = 32            # Data-Set-Ready Signal State
-MODEMSTATE_MASK_CTS = 16            # Clear-To-Send Signal State
-MODEMSTATE_MASK_CD_CHANGE = 8       # Delta Receive Line Signal Detect
-MODEMSTATE_MASK_RI_CHANGE = 4       # Trailing-edge Ring Detector
-MODEMSTATE_MASK_DSR_CHANGE = 2      # Delta Data-Set-Ready
-MODEMSTATE_MASK_CTS_CHANGE = 1      # Delta Clear-To-Send
-
-PURGE_RECEIVE_BUFFER = b'\x01'      # Purge access server receive data buffer
-PURGE_TRANSMIT_BUFFER = b'\x02'     # Purge access server transmit data buffer
-PURGE_BOTH_BUFFERS = b'\x03'        # Purge both the access server receive data
-                                    # buffer and the access server transmit data buffer
-
-
-RFC2217_PARITY_MAP = {
-    serial.PARITY_NONE: 1,
-    serial.PARITY_ODD: 2,
-    serial.PARITY_EVEN: 3,
-    serial.PARITY_MARK: 4,
-    serial.PARITY_SPACE: 5,
-}
-RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items())
-
-RFC2217_STOPBIT_MAP = {
-    serial.STOPBITS_ONE: 1,
-    serial.STOPBITS_ONE_POINT_FIVE: 3,
-    serial.STOPBITS_TWO: 2,
-}
-RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items())
-
-# Telnet filter states
-M_NORMAL = 0
-M_IAC_SEEN = 1
-M_NEGOTIATE = 2
-
-# TelnetOption and TelnetSubnegotiation states
-REQUESTED = 'REQUESTED'
-ACTIVE = 'ACTIVE'
-INACTIVE = 'INACTIVE'
-REALLY_INACTIVE = 'REALLY_INACTIVE'
-
-
-class TelnetOption(object):
-    """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
-
-    def __init__(self, connection, name, option, send_yes, send_no, ack_yes,
-                 ack_no, initial_state, activation_callback=None):
-        """\
-        Initialize option.
-        :param connection: connection used to transmit answers
-        :param name: a readable name for debug outputs
-        :param send_yes: what to send when option is to be enabled.
-        :param send_no: what to send when option is to be disabled.
-        :param ack_yes: what to expect when remote agrees on option.
-        :param ack_no: what to expect when remote disagrees on option.
-        :param initial_state: options initialized with REQUESTED are tried to
-            be enabled on startup. use INACTIVE for all others.
-        """
-        self.connection = connection
-        self.name = name
-        self.option = option
-        self.send_yes = send_yes
-        self.send_no = send_no
-        self.ack_yes = ack_yes
-        self.ack_no = ack_no
-        self.state = initial_state
-        self.active = False
-        self.activation_callback = activation_callback
-
-    def __repr__(self):
-        """String for debug outputs"""
-        return "{o.name}:{o.active}({o.state})".format(o=self)
-
-    def process_incoming(self, command):
-        """\
-        A DO/DONT/WILL/WONT was received for this option, update state and
-        answer when needed.
-        """
-        if command == self.ack_yes:
-            if self.state is REQUESTED:
-                self.state = ACTIVE
-                self.active = True
-                if self.activation_callback is not None:
-                    self.activation_callback()
-            elif self.state is ACTIVE:
-                pass
-            elif self.state is INACTIVE:
-                self.state = ACTIVE
-                self.connection.telnet_send_option(self.send_yes, self.option)
-                self.active = True
-                if self.activation_callback is not None:
-                    self.activation_callback()
-            elif self.state is REALLY_INACTIVE:
-                self.connection.telnet_send_option(self.send_no, self.option)
-            else:
-                raise ValueError('option in illegal state {!r}'.format(self))
-        elif command == self.ack_no:
-            if self.state is REQUESTED:
-                self.state = INACTIVE
-                self.active = False
-            elif self.state is ACTIVE:
-                self.state = INACTIVE
-                self.connection.telnet_send_option(self.send_no, self.option)
-                self.active = False
-            elif self.state is INACTIVE:
-                pass
-            elif self.state is REALLY_INACTIVE:
-                pass
-            else:
-                raise ValueError('option in illegal state {!r}'.format(self))
-
-
-class TelnetSubnegotiation(object):
-    """\
-    A object to handle subnegotiation of options. In this case actually
-    sub-sub options for RFC 2217. It is used to track com port options.
-    """
-
-    def __init__(self, connection, name, option, ack_option=None):
-        if ack_option is None:
-            ack_option = option
-        self.connection = connection
-        self.name = name
-        self.option = option
-        self.value = None
-        self.ack_option = ack_option
-        self.state = INACTIVE
-
-    def __repr__(self):
-        """String for debug outputs."""
-        return "{sn.name}:{sn.state}".format(sn=self)
-
-    def set(self, value):
-        """\
-        Request a change of the value. a request is sent to the server. if
-        the client needs to know if the change is performed he has to check the
-        state of this object.
-        """
-        self.value = value
-        self.state = REQUESTED
-        self.connection.rfc2217_send_subnegotiation(self.option, self.value)
-        if self.connection.logger:
-            self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value))
-
-    def is_ready(self):
-        """\
-        Check if answer from server has been received. when server rejects
-        the change, raise a ValueError.
-        """
-        if self.state == REALLY_INACTIVE:
-            raise ValueError("remote rejected value for option {!r}".format(self.name))
-        return self.state == ACTIVE
-    # add property to have a similar interface as TelnetOption
-    active = property(is_ready)
-
-    def wait(self, timeout=3):
-        """\
-        Wait until the subnegotiation has been acknowledged or timeout. It
-        can also throw a value error when the answer from the server does not
-        match the value sent.
-        """
-        timeout_time = time.time() + timeout
-        while time.time() < timeout_time:
-            time.sleep(0.05)    # prevent 100% CPU load
-            if self.is_ready():
-                break
-        else:
-            raise SerialException("timeout while waiting for option {!r}".format(self.name))
-
-    def check_answer(self, suboption):
-        """\
-        Check an incoming subnegotiation block. The parameter already has
-        cut off the header like sub option number and com port option value.
-        """
-        if self.value == suboption[:len(self.value)]:
-            self.state = ACTIVE
-        else:
-            # error propagation done in is_ready
-            self.state = REALLY_INACTIVE
-        if self.connection.logger:
-            self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state))
-
-
-class Serial(SerialBase):
-    """Serial port implementation for RFC 2217 remote serial ports."""
-
-    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
-                 9600, 19200, 38400, 57600, 115200)
-
-    def __init__(self, *args, **kwargs):
-        super(Serial, self).__init__(*args, **kwargs)
-        self._thread = None
-        self._socket = None
-        self._linestate = 0
-        self._modemstate = None
-        self._modemstate_expires = 0
-        self._remote_suspend_flow = False
-        self._write_lock = None
-        self.logger = None
-        self._ignore_set_control_answer = False
-        self._poll_modem_state = False
-        self._network_timeout = 3
-        self._telnet_options = None
-        self._rfc2217_port_settings = None
-        self._rfc2217_options = None
-        self._read_buffer = None
-
-    def open(self):
-        """\
-        Open port with current settings. This may throw a SerialException
-        if the port cannot be opened.
-        """
-        self.logger = None
-        self._ignore_set_control_answer = False
-        self._poll_modem_state = False
-        self._network_timeout = 3
-        if self._port is None:
-            raise SerialException("Port must be configured before it can be used.")
-        if self.is_open:
-            raise SerialException("Port is already open.")
-        try:
-            self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5)  # XXX good value?
-            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
-        except Exception as msg:
-            self._socket = None
-            raise SerialException("Could not open port {}: {}".format(self.portstr, msg))
-
-        # use a thread save queue as buffer. it also simplifies implementing
-        # the read timeout
-        self._read_buffer = Queue.Queue()
-        # to ensure that user writes does not interfere with internal
-        # telnet/rfc2217 options establish a lock
-        self._write_lock = threading.Lock()
-        # name the following separately so that, below, a check can be easily done
-        mandadory_options = [
-            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
-            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED),
-        ]
-        # all supported telnet options
-        self._telnet_options = [
-            TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED),
-            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
-            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED),
-            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE),
-            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED),
-        ] + mandadory_options
-        # RFC 2217 specific states
-        # COM port settings
-        self._rfc2217_port_settings = {
-            'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE),
-            'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE),
-            'parity':   TelnetSubnegotiation(self, 'parity',   SET_PARITY,   SERVER_SET_PARITY),
-            'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE),
-        }
-        # There are more subnegotiation objects, combine all in one dictionary
-        # for easy access
-        self._rfc2217_options = {
-            'purge':    TelnetSubnegotiation(self, 'purge',    PURGE_DATA,   SERVER_PURGE_DATA),
-            'control':  TelnetSubnegotiation(self, 'control',  SET_CONTROL,  SERVER_SET_CONTROL),
-        }
-        self._rfc2217_options.update(self._rfc2217_port_settings)
-        # cache for line and modem states that the server sends to us
-        self._linestate = 0
-        self._modemstate = None
-        self._modemstate_expires = 0
-        # RFC 2217 flow control between server and client
-        self._remote_suspend_flow = False
-
-        self.is_open = True
-        self._thread = threading.Thread(target=self._telnet_read_loop)
-        self._thread.setDaemon(True)
-        self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port))
-        self._thread.start()
-
-        try:    # must clean-up if open fails
-            # negotiate Telnet/RFC 2217 -> send initial requests
-            for option in self._telnet_options:
-                if option.state is REQUESTED:
-                    self.telnet_send_option(option.send_yes, option.option)
-            # now wait until important options are negotiated
-            timeout_time = time.time() + self._network_timeout
-            while time.time() < timeout_time:
-                time.sleep(0.05)    # prevent 100% CPU load
-                if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options):
-                    break
-            else:
-                raise SerialException(
-                    "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options))
-            if self.logger:
-                self.logger.info("Negotiated options: {}".format(self._telnet_options))
-
-            # fine, go on, set RFC 2271 specific things
-            self._reconfigure_port()
-            # all things set up get, now a clean start
-            if not self._dsrdtr:
-                self._update_dtr_state()
-            if not self._rtscts:
-                self._update_rts_state()
-            self.reset_input_buffer()
-            self.reset_output_buffer()
-        except:
-            self.close()
-            raise
-
-    def _reconfigure_port(self):
-        """Set communication parameters on opened port."""
-        if self._socket is None:
-            raise SerialException("Can only operate on open ports")
-
-        # if self._timeout != 0 and self._interCharTimeout is not None:
-            # XXX
-
-        if self._write_timeout is not None:
-            raise NotImplementedError('write_timeout is currently not supported')
-            # XXX
-
-        # Setup the connection
-        # to get good performance, all parameter changes are sent first...
-        if not 0 < self._baudrate < 2 ** 32:
-            raise ValueError("invalid baudrate: {!r}".format(self._baudrate))
-        self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate))
-        self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize))
-        self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity]))
-        self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits]))
-
-        # and now wait until parameters are active
-        items = self._rfc2217_port_settings.values()
-        if self.logger:
-            self.logger.debug("Negotiating settings: {}".format(items))
-        timeout_time = time.time() + self._network_timeout
-        while time.time() < timeout_time:
-            time.sleep(0.05)    # prevent 100% CPU load
-            if sum(o.active for o in items) == len(items):
-                break
-        else:
-            raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items))
-        if self.logger:
-            self.logger.info("Negotiated settings: {}".format(items))
-
-        if self._rtscts and self._xonxoff:
-            raise ValueError('xonxoff and rtscts together are not supported')
-        elif self._rtscts:
-            self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL)
-        elif self._xonxoff:
-            self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL)
-        else:
-            self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL)
-
-    def close(self):
-        """Close port"""
-        self.is_open = False
-        if self._socket:
-            try:
-                self._socket.shutdown(socket.SHUT_RDWR)
-                self._socket.close()
-            except:
-                # ignore errors.
-                pass
-        if self._thread:
-            self._thread.join(7)  # XXX more than socket timeout
-            self._thread = None
-            # in case of quick reconnects, give the server some time
-            time.sleep(0.3)
-        self._socket = None
-
-    def from_url(self, url):
-        """\
-        extract host and port from an URL string, other settings are extracted
-        an stored in instance
-        """
-        parts = urlparse.urlsplit(url)
-        if parts.scheme != "rfc2217":
-            raise SerialException(
-                'expected a string in the form '
-                '"rfc2217://<host>:<port>[?option[&option...]]": '
-                'not starting with rfc2217:// ({!r})'.format(parts.scheme))
-        try:
-            # process options now, directly altering self
-            for option, values in urlparse.parse_qs(parts.query, True).items():
-                if option == 'logging':
-                    logging.basicConfig()   # XXX is that good to call it here?
-                    self.logger = logging.getLogger('pySerial.rfc2217')
-                    self.logger.setLevel(LOGGER_LEVELS[values[0]])
-                    self.logger.debug('enabled logging')
-                elif option == 'ign_set_control':
-                    self._ignore_set_control_answer = True
-                elif option == 'poll_modem':
-                    self._poll_modem_state = True
-                elif option == 'timeout':
-                    self._network_timeout = float(values[0])
-                else:
-                    raise ValueError('unknown option: {!r}'.format(option))
-            if not 0 <= parts.port < 65536:
-                raise ValueError("port not in range 0...65535")
-        except ValueError as e:
-            raise SerialException(
-                'expected a string in the form '
-                '"rfc2217://<host>:<port>[?option[&option...]]": {}'.format(e))
-        return (parts.hostname, parts.port)
-
-    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
-
-    @property
-    def in_waiting(self):
-        """Return the number of bytes currently in the input buffer."""
-        if not self.is_open:
-            raise portNotOpenError
-        return self._read_buffer.qsize()
-
-    def read(self, size=1):
-        """\
-        Read size bytes from the serial port. If a timeout is set it may
-        return less characters as requested. With no timeout it will block
-        until the requested number of bytes is read.
-        """
-        if not self.is_open:
-            raise portNotOpenError
-        data = bytearray()
-        try:
-            while len(data) < size:
-                if self._thread is None:
-                    raise SerialException('connection failed (reader thread died)')
-                data += self._read_buffer.get(True, self._timeout)
-        except Queue.Empty:  # -> timeout
-            pass
-        return bytes(data)
-
-    def write(self, data):
-        """\
-        Output the given byte string over the serial port. Can block if the
-        connection is blocked. May raise SerialException if the connection is
-        closed.
-        """
-        if not self.is_open:
-            raise portNotOpenError
-        with self._write_lock:
-            try:
-                self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED))
-            except socket.error as e:
-                raise SerialException("connection failed (socket error): {}".format(e))
-        return len(data)
-
-    def reset_input_buffer(self):
-        """Clear input buffer, discarding all that is in the buffer."""
-        if not self.is_open:
-            raise portNotOpenError
-        self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER)
-        # empty read buffer
-        while self._read_buffer.qsize():
-            self._read_buffer.get(False)
-
-    def reset_output_buffer(self):
-        """\
-        Clear output buffer, aborting the current output and
-        discarding all that is in the buffer.
-        """
-        if not self.is_open:
-            raise portNotOpenError
-        self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER)
-
-    def _update_break_state(self):
-        """\
-        Set break: Controls TXD. When active, to transmitting is
-        possible.
-        """
-        if not self.is_open:
-            raise portNotOpenError
-        if self.logger:
-            self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive'))
-        if self._break_state:
-            self.rfc2217_set_control(SET_CONTROL_BREAK_ON)
-        else:
-            self.rfc2217_set_control(SET_CONTROL_BREAK_OFF)
-
-    def _update_rts_state(self):
-        """Set terminal status line: Request To Send."""
-        if not self.is_open:
-            raise portNotOpenError
-        if self.logger:
-            self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive'))
-        if self._rts_state:
-            self.rfc2217_set_control(SET_CONTROL_RTS_ON)
-        else:
-            self.rfc2217_set_control(SET_CONTROL_RTS_OFF)
-
-    def _update_dtr_state(self):
-        """Set terminal status line: Data Terminal Ready."""
-        if not self.is_open:
-            raise portNotOpenError
-        if self.logger:
-            self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive'))
-        if self._dtr_state:
-            self.rfc2217_set_control(SET_CONTROL_DTR_ON)
-        else:
-            self.rfc2217_set_control(SET_CONTROL_DTR_OFF)
-
-    @property
-    def cts(self):
-        """Read terminal status line: Clear To Send."""
-        if not self.is_open:
-            raise portNotOpenError
-        return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS)
-
-    @property
-    def dsr(self):
-        """Read terminal status line: Data Set Ready."""
-        if not self.is_open:
-            raise portNotOpenError
-        return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR)
-
-    @property
-    def ri(self):
-        """Read terminal status line: Ring Indicator."""
-        if not self.is_open:
-            raise portNotOpenError
-        return bool(self.get_modem_state() & MODEMSTATE_MASK_RI)
-
-    @property
-    def cd(self):
-        """Read terminal status line: Carrier Detect."""
-        if not self.is_open:
-            raise portNotOpenError
-        return bool(self.get_modem_state() & MODEMSTATE_MASK_CD)
-
-    # - - - platform specific - - -
-    # None so far
-
-    # - - - RFC2217 specific - - -
-
-    def _telnet_read_loop(self):
-        """Read loop for the socket."""
-        mode = M_NORMAL
-        suboption = None
-        try:
-            while self.is_open:
-                try:
-                    data = self._socket.recv(1024)
-                except socket.timeout:
-                    # just need to get out of recv form time to time to check if
-                    # still alive
-                    continue
-                except socket.error as e:
-                    # connection fails -> terminate loop
-                    if self.logger:
-                        self.logger.debug("socket error in reader thread: {}".format(e))
-                    break
-                if not data:
-                    break  # lost connection
-                for byte in iterbytes(data):
-                    if mode == M_NORMAL:
-                        # interpret as command or as data
-                        if byte == IAC:
-                            mode = M_IAC_SEEN
-                        else:
-                            # store data in read buffer or sub option buffer
-                            # depending on state
-                            if suboption is not None:
-                                suboption += byte
-                            else:
-                                self._read_buffer.put(byte)
-                    elif mode == M_IAC_SEEN:
-                        if byte == IAC:
-                            # interpret as command doubled -> insert character
-                            # itself
-                            if suboption is not None:
-                                suboption += IAC
-                            else:
-                                self._read_buffer.put(IAC)
-                            mode = M_NORMAL
-                        elif byte == SB:
-                            # sub option start
-                            suboption = bytearray()
-                            mode = M_NORMAL
-                        elif byte == SE:
-                            # sub option end -> process it now
-                            self._telnet_process_subnegotiation(bytes(suboption))
-                            suboption = None
-                            mode = M_NORMAL
-                        elif byte in (DO, DONT, WILL, WONT):
-                            # negotiation
-                            telnet_command = byte
-                            mode = M_NEGOTIATE
-                        else:
-                            # other telnet commands
-                            self._telnet_process_command(byte)
-                            mode = M_NORMAL
-                    elif mode == M_NEGOTIATE:  # DO, DONT, WILL, WONT was received, option now following
-                        self._telnet_negotiate_option(telnet_command, byte)
-                        mode = M_NORMAL
-        finally:
-            self._thread = None
-            if self.logger:
-                self.logger.debug("read thread terminated")
-
-    # - incoming telnet commands and options
-
-    def _telnet_process_command(self, command):
-        """Process commands other than DO, DONT, WILL, WONT."""
-        # Currently none. RFC2217 only uses negotiation and subnegotiation.
-        if self.logger:
-            self.logger.warning("ignoring Telnet command: {!r}".format(command))
-
-    def _telnet_negotiate_option(self, command, option):
-        """Process incoming DO, DONT, WILL, WONT."""
-        # check our registered telnet options and forward command to them
-        # they know themselves if they have to answer or not
-        known = False
-        for item in self._telnet_options:
-            # can have more than one match! as some options are duplicated for
-            # 'us' and 'them'
-            if item.option == option:
-                item.process_incoming(command)
-                known = True
-        if not known:
-            # handle unknown options
-            # only answer to positive requests and deny them
-            if command == WILL or command == DO:
-                self.telnet_send_option((DONT if command == WILL else WONT), option)
-                if self.logger:
-                    self.logger.warning("rejected Telnet option: {!r}".format(option))
-
-    def _telnet_process_subnegotiation(self, suboption):
-        """Process subnegotiation, the data between IAC SB and IAC SE."""
-        if suboption[0:1] == COM_PORT_OPTION:
-            if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3:
-                self._linestate = ord(suboption[2:3])  # ensure it is a number
-                if self.logger:
-                    self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate))
-            elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3:
-                self._modemstate = ord(suboption[2:3])  # ensure it is a number
-                if self.logger:
-                    self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate))
-                # update time when we think that a poll would make sense
-                self._modemstate_expires = time.time() + 0.3
-            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
-                self._remote_suspend_flow = True
-            elif suboption[1:2] == FLOWCONTROL_RESUME:
-                self._remote_suspend_flow = False
-            else:
-                for item in self._rfc2217_options.values():
-                    if item.ack_option == suboption[1:2]:
-                        #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
-                        item.check_answer(bytes(suboption[2:]))
-                        break
-                else:
-                    if self.logger:
-                        self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption))
-        else:
-            if self.logger:
-                self.logger.warning("ignoring subnegotiation: {!r}".format(suboption))
-
-    # - outgoing telnet commands and options
-
-    def _internal_raw_write(self, data):
-        """internal socket write with no data escaping. used to send telnet stuff."""
-        with self._write_lock:
-            self._socket.sendall(data)
-
-    def telnet_send_option(self, action, option):
-        """Send DO, DONT, WILL, WONT."""
-        self._internal_raw_write(to_bytes([IAC, action, option]))
-
-    def rfc2217_send_subnegotiation(self, option, value=b''):
-        """Subnegotiation of RFC2217 parameters."""
-        value = value.replace(IAC, IAC_DOUBLED)
-        self._internal_raw_write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
-
-    def rfc2217_send_purge(self, value):
-        """\
-        Send purge request to the remote.
-        (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS)
-        """
-        item = self._rfc2217_options['purge']
-        item.set(value)  # transmit desired purge type
-        item.wait(self._network_timeout)  # wait for acknowledge from the server
-
-    def rfc2217_set_control(self, value):
-        """transmit change of control line to remote"""
-        item = self._rfc2217_options['control']
-        item.set(value)  # transmit desired control type
-        if self._ignore_set_control_answer:
-            # answers are ignored when option is set. compatibility mode for
-            # servers that answer, but not the expected one... (or no answer
-            # at all) i.e. sredird
-            time.sleep(0.1)  # this helps getting the unit tests passed
-        else:
-            item.wait(self._network_timeout)  # wait for acknowledge from the server
-
-    def rfc2217_flow_server_ready(self):
-        """\
-        check if server is ready to receive data. block for some time when
-        not.
-        """
-        #~ if self._remote_suspend_flow:
-        #~     wait---
-
-    def get_modem_state(self):
-        """\
-        get last modem state (cached value. If value is "old", request a new
-        one. This cache helps that we don't issue to many requests when e.g. all
-        status lines, one after the other is queried by the user (getCTS, getDSR
-        etc.)
-        """
-        # active modem state polling enabled? is the value fresh enough?
-        if self._poll_modem_state and self._modemstate_expires < time.time():
-            if self.logger:
-                self.logger.debug('polling modem state')
-            # when it is older, request an update
-            self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE)
-            timeout_time = time.time() + self._network_timeout
-            while time.time() < timeout_time:
-                time.sleep(0.05)    # prevent 100% CPU load
-                # when expiration time is updated, it means that there is a new
-                # value
-                if self._modemstate_expires > time.time():
-                    break
-            else:
-                if self.logger:
-                    self.logger.warning('poll for modem state failed')
-            # even when there is a timeout, do not generate an error just
-            # return the last known value. this way we can support buggy
-            # servers that do not respond to polls, but send automatic
-            # updates.
-        if self._modemstate is not None:
-            if self.logger:
-                self.logger.debug('using cached modem state')
-            return self._modemstate
-        else:
-            # never received a notification from the server
-            raise SerialException("remote sends no NOTIFY_MODEMSTATE")
-
-
-#############################################################################
-# The following is code that helps implementing an RFC 2217 server.
-
-class PortManager(object):
-    """\
-    This class manages the state of Telnet and RFC 2217. It needs a serial
-    instance and a connection to work with. Connection is expected to implement
-    a (thread safe) write function, that writes the string to the network.
-    """
-
-    def __init__(self, serial_port, connection, logger=None):
-        self.serial = serial_port
-        self.connection = connection
-        self.logger = logger
-        self._client_is_rfc2217 = False
-
-        # filter state machine
-        self.mode = M_NORMAL
-        self.suboption = None
-        self.telnet_command = None
-
-        # states for modem/line control events
-        self.modemstate_mask = 255
-        self.last_modemstate = None
-        self.linstate_mask = 0
-
-        # all supported telnet options
-        self._telnet_options = [
-            TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
-            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
-            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
-            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
-            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED),
-            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok),
-            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok),
-        ]
-
-        # negotiate Telnet/RFC2217 -> send initial requests
-        if self.logger:
-            self.logger.debug("requesting initial Telnet/RFC 2217 options")
-        for option in self._telnet_options:
-            if option.state is REQUESTED:
-                self.telnet_send_option(option.send_yes, option.option)
-        # issue 1st modem state notification
-
-    def _client_ok(self):
-        """\
-        callback of telnet option. It gets called when option is activated.
-        This one here is used to detect when the client agrees on RFC 2217. A
-        flag is set so that other functions like check_modem_lines know if the
-        client is OK.
-        """
-        # The callback is used for we and they so if one party agrees, we're
-        # already happy. it seems not all servers do the negotiation correctly
-        # and i guess there are incorrect clients too.. so be happy if client
-        # answers one or the other positively.
-        self._client_is_rfc2217 = True
-        if self.logger:
-            self.logger.info("client accepts RFC 2217")
-        # this is to ensure that the client gets a notification, even if there
-        # was no change
-        self.check_modem_lines(force_notification=True)
-
-    # - outgoing telnet commands and options
-
-    def telnet_send_option(self, action, option):
-        """Send DO, DONT, WILL, WONT."""
-        self.connection.write(to_bytes([IAC, action, option]))
-
-    def rfc2217_send_subnegotiation(self, option, value=b''):
-        """Subnegotiation of RFC 2217 parameters."""
-        value = value.replace(IAC, IAC_DOUBLED)
-        self.connection.write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
-
-    # - check modem lines, needs to be called periodically from user to
-    # establish polling
-
-    def check_modem_lines(self, force_notification=False):
-        """\
-        read control lines from serial port and compare the last value sent to remote.
-        send updates on changes.
-        """
-        modemstate = (
-            (self.serial.getCTS() and MODEMSTATE_MASK_CTS) |
-            (self.serial.getDSR() and MODEMSTATE_MASK_DSR) |
-            (self.serial.getRI() and MODEMSTATE_MASK_RI) |
-            (self.serial.getCD() and MODEMSTATE_MASK_CD))
-        # check what has changed
-        deltas = modemstate ^ (self.last_modemstate or 0)  # when last is None -> 0
-        if deltas & MODEMSTATE_MASK_CTS:
-            modemstate |= MODEMSTATE_MASK_CTS_CHANGE
-        if deltas & MODEMSTATE_MASK_DSR:
-            modemstate |= MODEMSTATE_MASK_DSR_CHANGE
-        if deltas & MODEMSTATE_MASK_RI:
-            modemstate |= MODEMSTATE_MASK_RI_CHANGE
-        if deltas & MODEMSTATE_MASK_CD:
-            modemstate |= MODEMSTATE_MASK_CD_CHANGE
-        # if new state is different and the mask allows this change, send
-        # notification. suppress notifications when client is not rfc2217
-        if modemstate != self.last_modemstate or force_notification:
-            if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification:
-                self.rfc2217_send_subnegotiation(
-                    SERVER_NOTIFY_MODEMSTATE,
-                    to_bytes([modemstate & self.modemstate_mask]))
-                if self.logger:
-                    self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate))
-            # save last state, but forget about deltas.
-            # otherwise it would also notify about changing deltas which is
-            # probably not very useful
-            self.last_modemstate = modemstate & 0xf0
-
-    # - outgoing data escaping
-
-    def escape(self, data):
-        """\
-        This generator function is for the user. All outgoing data has to be
-        properly escaped, so that no IAC character in the data stream messes up
-        the Telnet state machine in the server.
-
-        socket.sendall(escape(data))
-        """
-        for byte in iterbytes(data):
-            if byte == IAC:
-                yield IAC
-                yield IAC
-            else:
-                yield byte
-
-    # - incoming data filter
-
-    def filter(self, data):
-        """\
-        Handle a bunch of incoming bytes. This is a generator. It will yield
-        all characters not of interest for Telnet/RFC 2217.
-
-        The idea is that the reader thread pushes data from the socket through
-        this filter:
-
-        for byte in filter(socket.recv(1024)):
-            # do things like CR/LF conversion/whatever
-            # and write data to the serial port
-            serial.write(byte)
-
-        (socket error handling code left as exercise for the reader)
-        """
-        for byte in iterbytes(data):
-            if self.mode == M_NORMAL:
-                # interpret as command or as data
-                if byte == IAC:
-                    self.mode = M_IAC_SEEN
-                else:
-                    # store data in sub option buffer or pass it to our
-                    # consumer depending on state
-                    if self.suboption is not None:
-                        self.suboption += byte
-                    else:
-                        yield byte
-            elif self.mode == M_IAC_SEEN:
-                if byte == IAC:
-                    # interpret as command doubled -> insert character
-                    # itself
-                    if self.suboption is not None:
-                        self.suboption += byte
-                    else:
-                        yield byte
-                    self.mode = M_NORMAL
-                elif byte == SB:
-                    # sub option start
-                    self.suboption = bytearray()
-                    self.mode = M_NORMAL
-                elif byte == SE:
-                    # sub option end -> process it now
-                    self._telnet_process_subnegotiation(bytes(self.suboption))
-                    self.suboption = None
-                    self.mode = M_NORMAL
-                elif byte in (DO, DONT, WILL, WONT):
-                    # negotiation
-                    self.telnet_command = byte
-                    self.mode = M_NEGOTIATE
-                else:
-                    # other telnet commands
-                    self._telnet_process_command(byte)
-                    self.mode = M_NORMAL
-            elif self.mode == M_NEGOTIATE:  # DO, DONT, WILL, WONT was received, option now following
-                self._telnet_negotiate_option(self.telnet_command, byte)
-                self.mode = M_NORMAL
-
-    # - incoming telnet commands and options
-
-    def _telnet_process_command(self, command):
-        """Process commands other than DO, DONT, WILL, WONT."""
-        # Currently none. RFC2217 only uses negotiation and subnegotiation.
-        if self.logger:
-            self.logger.warning("ignoring Telnet command: {!r}".format(command))
-
-    def _telnet_negotiate_option(self, command, option):
-        """Process incoming DO, DONT, WILL, WONT."""
-        # check our registered telnet options and forward command to them
-        # they know themselves if they have to answer or not
-        known = False
-        for item in self._telnet_options:
-            # can have more than one match! as some options are duplicated for
-            # 'us' and 'them'
-            if item.option == option:
-                item.process_incoming(command)
-                known = True
-        if not known:
-            # handle unknown options
-            # only answer to positive requests and deny them
-            if command == WILL or command == DO:
-                self.telnet_send_option((DONT if command == WILL else WONT), option)
-                if self.logger:
-                    self.logger.warning("rejected Telnet option: {!r}".format(option))
-
-    def _telnet_process_subnegotiation(self, suboption):
-        """Process subnegotiation, the data between IAC SB and IAC SE."""
-        if suboption[0:1] == COM_PORT_OPTION:
-            if self.logger:
-                self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption))
-            if suboption[1:2] == SET_BAUDRATE:
-                backup = self.serial.baudrate
-                try:
-                    (baudrate,) = struct.unpack(b"!I", suboption[2:6])
-                    if baudrate != 0:
-                        self.serial.baudrate = baudrate
-                except ValueError as e:
-                    if self.logger:
-                        self.logger.error("failed to set baud rate: {}".format(e))
-                    self.serial.baudrate = backup
-                else:
-                    if self.logger:
-                        self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate))
-                self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate))
-            elif suboption[1:2] == SET_DATASIZE:
-                backup = self.serial.bytesize
-                try:
-                    (datasize,) = struct.unpack(b"!B", suboption[2:3])
-                    if datasize != 0:
-                        self.serial.bytesize = datasize
-                except ValueError as e:
-                    if self.logger:
-                        self.logger.error("failed to set data size: {}".format(e))
-                    self.serial.bytesize = backup
-                else:
-                    if self.logger:
-                        self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize))
-                self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize))
-            elif suboption[1:2] == SET_PARITY:
-                backup = self.serial.parity
-                try:
-                    parity = struct.unpack(b"!B", suboption[2:3])[0]
-                    if parity != 0:
-                        self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity]
-                except ValueError as e:
-                    if self.logger:
-                        self.logger.error("failed to set parity: {}".format(e))
-                    self.serial.parity = backup
-                else:
-                    if self.logger:
-                        self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity))
-                self.rfc2217_send_subnegotiation(
-                    SERVER_SET_PARITY,
-                    struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity]))
-            elif suboption[1:2] == SET_STOPSIZE:
-                backup = self.serial.stopbits
-                try:
-                    stopbits = struct.unpack(b"!B", suboption[2:3])[0]
-                    if stopbits != 0:
-                        self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits]
-                except ValueError as e:
-                    if self.logger:
-                        self.logger.error("failed to set stop bits: {}".format(e))
-                    self.serial.stopbits = backup
-                else:
-                    if self.logger:
-                        self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits))
-                self.rfc2217_send_subnegotiation(
-                    SERVER_SET_STOPSIZE,
-                    struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits]))
-            elif suboption[1:2] == SET_CONTROL:
-                if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING:
-                    if self.serial.xonxoff:
-                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
-                    elif self.serial.rtscts:
-                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
-                    else:
-                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
-                elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL:
-                    self.serial.xonxoff = False
-                    self.serial.rtscts = False
-                    if self.logger:
-                        self.logger.info("changed flow control to None")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
-                elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL:
-                    self.serial.xonxoff = True
-                    if self.logger:
-                        self.logger.info("changed flow control to XON/XOFF")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
-                elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL:
-                    self.serial.rtscts = True
-                    if self.logger:
-                        self.logger.info("changed flow control to RTS/CTS")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
-                elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE:
-                    if self.logger:
-                        self.logger.warning("requested break state - not implemented")
-                    pass  # XXX needs cached value
-                elif suboption[2:3] == SET_CONTROL_BREAK_ON:
-                    self.serial.setBreak(True)
-                    if self.logger:
-                        self.logger.info("changed BREAK to active")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON)
-                elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
-                    self.serial.setBreak(False)
-                    if self.logger:
-                        self.logger.info("changed BREAK to inactive")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF)
-                elif suboption[2:3] == SET_CONTROL_REQ_DTR:
-                    if self.logger:
-                        self.logger.warning("requested DTR state - not implemented")
-                    pass  # XXX needs cached value
-                elif suboption[2:3] == SET_CONTROL_DTR_ON:
-                    self.serial.setDTR(True)
-                    if self.logger:
-                        self.logger.info("changed DTR to active")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON)
-                elif suboption[2:3] == SET_CONTROL_DTR_OFF:
-                    self.serial.setDTR(False)
-                    if self.logger:
-                        self.logger.info("changed DTR to inactive")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF)
-                elif suboption[2:3] == SET_CONTROL_REQ_RTS:
-                    if self.logger:
-                        self.logger.warning("requested RTS state - not implemented")
-                    pass  # XXX needs cached value
-                    #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
-                elif suboption[2:3] == SET_CONTROL_RTS_ON:
-                    self.serial.setRTS(True)
-                    if self.logger:
-                        self.logger.info("changed RTS to active")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
-                elif suboption[2:3] == SET_CONTROL_RTS_OFF:
-                    self.serial.setRTS(False)
-                    if self.logger:
-                        self.logger.info("changed RTS to inactive")
-                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF)
-                #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
-                #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
-            elif suboption[1:2] == NOTIFY_LINESTATE:
-                # client polls for current state
-                self.rfc2217_send_subnegotiation(
-                    SERVER_NOTIFY_LINESTATE,
-                    to_bytes([0]))   # sorry, nothing like that implemented
-            elif suboption[1:2] == NOTIFY_MODEMSTATE:
-                if self.logger:
-                    self.logger.info("request for modem state")
-                # client polls for current state
-                self.check_modem_lines(force_notification=True)
-            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
-                if self.logger:
-                    self.logger.info("suspend")
-                self._remote_suspend_flow = True
-            elif suboption[1:2] == FLOWCONTROL_RESUME:
-                if self.logger:
-                    self.logger.info("resume")
-                self._remote_suspend_flow = False
-            elif suboption[1:2] == SET_LINESTATE_MASK:
-                self.linstate_mask = ord(suboption[2:3])  # ensure it is a number
-                if self.logger:
-                    self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask))
-            elif suboption[1:2] == SET_MODEMSTATE_MASK:
-                self.modemstate_mask = ord(suboption[2:3])  # ensure it is a number
-                if self.logger:
-                    self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask))
-            elif suboption[1:2] == PURGE_DATA:
-                if suboption[2:3] == PURGE_RECEIVE_BUFFER:
-                    self.serial.reset_input_buffer()
-                    if self.logger:
-                        self.logger.info("purge in")
-                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER)
-                elif suboption[2:3] == PURGE_TRANSMIT_BUFFER:
-                    self.serial.reset_output_buffer()
-                    if self.logger:
-                        self.logger.info("purge out")
-                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER)
-                elif suboption[2:3] == PURGE_BOTH_BUFFERS:
-                    self.serial.reset_input_buffer()
-                    self.serial.reset_output_buffer()
-                    if self.logger:
-                        self.logger.info("purge both")
-                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS)
-                else:
-                    if self.logger:
-                        self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:])))
-            else:
-                if self.logger:
-                    self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:])))
-        else:
-            if self.logger:
-                self.logger.warning("unknown subnegotiation: {!r}".format(suboption))
-
-
-# simple client test
-if __name__ == '__main__':
-    import sys
-    s = Serial('rfc2217://localhost:7000', 115200)
-    sys.stdout.write('{}\n'.format(s))
-
-    sys.stdout.write("write...\n")
-    s.write(b"hello\n")
-    s.flush()
-    sys.stdout.write("read: {}\n".format(s.read(5)))
-    s.close()