Home | History | Annotate | Download | only in serial
      1 #! python
      2 #
      3 # Python Serial Port Extension for Win32, Linux, BSD, Jython
      4 # see __init__.py
      5 #
      6 # This module implements a RFC2217 compatible client. RF2217 descibes a
      7 # protocol to access serial ports over TCP/IP and allows setting the baud rate,
      8 # modem control lines etc.
      9 #
     10 # (C) 2001-2013 Chris Liechti <cliechti (at] gmx.net>
     11 # this is distributed under a free software license, see license.txt
     12 
     13 # TODO:
     14 # - setting control line -> answer is not checked (had problems with one of the
     15 #   severs). consider implementing a compatibility mode flag to make check
     16 #   conditional
     17 # - write timeout not implemented at all
     18 
     19 ##############################################################################
     20 # observations and issues with servers
     21 #=============================================================================
     22 # sredird V2.2.1
     23 # - http://www.ibiblio.org/pub/Linux/system/serial/   sredird-2.2.2.tar.gz
     24 # - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding
     25 #   [105 1] instead of the actual value.
     26 # - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger
     27 #   numbers than 2**32?
     28 # - To get the signature [COM_PORT_OPTION 0] has to be sent.
     29 # - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done
     30 #=============================================================================
     31 # telnetcpcd (untested)
     32 # - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz
     33 # - To get the signature [COM_PORT_OPTION] w/o data has to be sent.
     34 #=============================================================================
     35 # ser2net
     36 # - does not negotiate BINARY or COM_PORT_OPTION for his side but at least
     37 #   acknowledges that the client activates these options
     38 # - The configuration may be that the server prints a banner. As this client
     39 #   implementation does a flushInput on connect, this banner is hidden from
     40 #   the user application.
     41 # - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one
     42 #   second.
     43 # - To get the signature [COM_PORT_OPTION 0] has to be sent.
     44 # - run a server: run ser2net daemon, in /etc/ser2net.conf:
     45 #     2000:telnet:0:/dev/ttyS0:9600 remctl banner
     46 ##############################################################################
     47 
     48 # How to identify ports? pySerial might want to support other protocols in the
     49 # future, so lets use an URL scheme.
     50 # for RFC2217 compliant servers we will use this:
     51 #    rfc2217://<host>:<port>[/option[/option...]]
     52 #
     53 # options:
     54 # - "debug" print diagnostic messages
     55 # - "ign_set_control": do not look at the answers to SET_CONTROL
     56 # - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read.
     57 #   Without this option it expects that the server sends notifications
     58 #   automatically on change (which most servers do and is according to the
     59 #   RFC).
     60 # the order of the options is not relevant
     61 
     62 from serial.serialutil import *
     63 import time
     64 import struct
     65 import socket
     66 import threading
     67 import Queue
     68 import logging
     69 
     70 # port string is expected to be something like this:
     71 # rfc2217://host:port
     72 # host may be an IP or including domain, whatever.
     73 # port is 0...65535
     74 
     75 # map log level names to constants. used in fromURL()
     76 LOGGER_LEVELS = {
     77     'debug': logging.DEBUG,
     78     'info': logging.INFO,
     79     'warning': logging.WARNING,
     80     'error': logging.ERROR,
     81     }
     82 
     83 
     84 # telnet protocol characters
     85 IAC  = to_bytes([255]) # Interpret As Command
     86 DONT = to_bytes([254])
     87 DO   = to_bytes([253])
     88 WONT = to_bytes([252])
     89 WILL = to_bytes([251])
     90 IAC_DOUBLED = to_bytes([IAC, IAC])
     91 
     92 SE  = to_bytes([240])  # Subnegotiation End
     93 NOP = to_bytes([241])  # No Operation
     94 DM  = to_bytes([242])  # Data Mark
     95 BRK = to_bytes([243])  # Break
     96 IP  = to_bytes([244])  # Interrupt process
     97 AO  = to_bytes([245])  # Abort output
     98 AYT = to_bytes([246])  # Are You There
     99 EC  = to_bytes([247])  # Erase Character
    100 EL  = to_bytes([248])  # Erase Line
    101 GA  = to_bytes([249])  # Go Ahead
    102 SB =  to_bytes([250])  # Subnegotiation Begin
    103 
    104 # selected telnet options
    105 BINARY = to_bytes([0]) # 8-bit data path
    106 ECHO = to_bytes([1])   # echo
    107 SGA = to_bytes([3])    # suppress go ahead
    108 
    109 # RFC2217
    110 COM_PORT_OPTION = to_bytes([44])
    111 
    112 # Client to Access Server
    113 SET_BAUDRATE = to_bytes([1])
    114 SET_DATASIZE = to_bytes([2])
    115 SET_PARITY = to_bytes([3])
    116 SET_STOPSIZE = to_bytes([4])
    117 SET_CONTROL = to_bytes([5])
    118 NOTIFY_LINESTATE = to_bytes([6])
    119 NOTIFY_MODEMSTATE = to_bytes([7])
    120 FLOWCONTROL_SUSPEND = to_bytes([8])
    121 FLOWCONTROL_RESUME = to_bytes([9])
    122 SET_LINESTATE_MASK = to_bytes([10])
    123 SET_MODEMSTATE_MASK = to_bytes([11])
    124 PURGE_DATA = to_bytes([12])
    125 
    126 SERVER_SET_BAUDRATE = to_bytes([101])
    127 SERVER_SET_DATASIZE = to_bytes([102])
    128 SERVER_SET_PARITY = to_bytes([103])
    129 SERVER_SET_STOPSIZE = to_bytes([104])
    130 SERVER_SET_CONTROL = to_bytes([105])
    131 SERVER_NOTIFY_LINESTATE = to_bytes([106])
    132 SERVER_NOTIFY_MODEMSTATE = to_bytes([107])
    133 SERVER_FLOWCONTROL_SUSPEND = to_bytes([108])
    134 SERVER_FLOWCONTROL_RESUME = to_bytes([109])
    135 SERVER_SET_LINESTATE_MASK = to_bytes([110])
    136 SERVER_SET_MODEMSTATE_MASK = to_bytes([111])
    137 SERVER_PURGE_DATA = to_bytes([112])
    138 
    139 RFC2217_ANSWER_MAP = {
    140     SET_BAUDRATE: SERVER_SET_BAUDRATE,
    141     SET_DATASIZE: SERVER_SET_DATASIZE,
    142     SET_PARITY: SERVER_SET_PARITY,
    143     SET_STOPSIZE: SERVER_SET_STOPSIZE,
    144     SET_CONTROL: SERVER_SET_CONTROL,
    145     NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE,
    146     NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE,
    147     FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND,
    148     FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME,
    149     SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK,
    150     SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK,
    151     PURGE_DATA: SERVER_PURGE_DATA,
    152 }
    153 
    154 SET_CONTROL_REQ_FLOW_SETTING = to_bytes([0])        # Request Com Port Flow Control Setting (outbound/both)
    155 SET_CONTROL_USE_NO_FLOW_CONTROL = to_bytes([1])     # Use No Flow Control (outbound/both)
    156 SET_CONTROL_USE_SW_FLOW_CONTROL = to_bytes([2])     # Use XON/XOFF Flow Control (outbound/both)
    157 SET_CONTROL_USE_HW_FLOW_CONTROL = to_bytes([3])     # Use HARDWARE Flow Control (outbound/both)
    158 SET_CONTROL_REQ_BREAK_STATE = to_bytes([4])         # Request BREAK State
    159 SET_CONTROL_BREAK_ON = to_bytes([5])                # Set BREAK State ON
    160 SET_CONTROL_BREAK_OFF = to_bytes([6])               # Set BREAK State OFF
    161 SET_CONTROL_REQ_DTR = to_bytes([7])                 # Request DTR Signal State
    162 SET_CONTROL_DTR_ON = to_bytes([8])                  # Set DTR Signal State ON
    163 SET_CONTROL_DTR_OFF = to_bytes([9])                 # Set DTR Signal State OFF
    164 SET_CONTROL_REQ_RTS = to_bytes([10])                # Request RTS Signal State
    165 SET_CONTROL_RTS_ON = to_bytes([11])                 # Set RTS Signal State ON
    166 SET_CONTROL_RTS_OFF = to_bytes([12])                # Set RTS Signal State OFF
    167 SET_CONTROL_REQ_FLOW_SETTING_IN = to_bytes([13])    # Request Com Port Flow Control Setting (inbound)
    168 SET_CONTROL_USE_NO_FLOW_CONTROL_IN = to_bytes([14]) # Use No Flow Control (inbound)
    169 SET_CONTROL_USE_SW_FLOW_CONTOL_IN = to_bytes([15])  # Use XON/XOFF Flow Control (inbound)
    170 SET_CONTROL_USE_HW_FLOW_CONTOL_IN = to_bytes([16])  # Use HARDWARE Flow Control (inbound)
    171 SET_CONTROL_USE_DCD_FLOW_CONTROL = to_bytes([17])   # Use DCD Flow Control (outbound/both)
    172 SET_CONTROL_USE_DTR_FLOW_CONTROL = to_bytes([18])   # Use DTR Flow Control (inbound)
    173 SET_CONTROL_USE_DSR_FLOW_CONTROL = to_bytes([19])   # Use DSR Flow Control (outbound/both)
    174 
    175 LINESTATE_MASK_TIMEOUT = 128                # Time-out Error
    176 LINESTATE_MASK_SHIFTREG_EMPTY = 64          # Transfer Shift Register Empty
    177 LINESTATE_MASK_TRANSREG_EMPTY = 32          # Transfer Holding Register Empty
    178 LINESTATE_MASK_BREAK_DETECT = 16            # Break-detect Error
    179 LINESTATE_MASK_FRAMING_ERROR = 8            # Framing Error
    180 LINESTATE_MASK_PARTIY_ERROR = 4             # Parity Error
    181 LINESTATE_MASK_OVERRUN_ERROR = 2            # Overrun Error
    182 LINESTATE_MASK_DATA_READY = 1               # Data Ready
    183 
    184 MODEMSTATE_MASK_CD = 128                    # Receive Line Signal Detect (also known as Carrier Detect)
    185 MODEMSTATE_MASK_RI = 64                     # Ring Indicator
    186 MODEMSTATE_MASK_DSR = 32                    # Data-Set-Ready Signal State
    187 MODEMSTATE_MASK_CTS = 16                    # Clear-To-Send Signal State
    188 MODEMSTATE_MASK_CD_CHANGE = 8               # Delta Receive Line Signal Detect
    189 MODEMSTATE_MASK_RI_CHANGE = 4               # Trailing-edge Ring Detector
    190 MODEMSTATE_MASK_DSR_CHANGE = 2              # Delta Data-Set-Ready
    191 MODEMSTATE_MASK_CTS_CHANGE = 1              # Delta Clear-To-Send
    192 
    193 PURGE_RECEIVE_BUFFER = to_bytes([1])        # Purge access server receive data buffer
    194 PURGE_TRANSMIT_BUFFER = to_bytes([2])       # Purge access server transmit data buffer
    195 PURGE_BOTH_BUFFERS = to_bytes([3])          # Purge both the access server receive data buffer and the access server transmit data buffer
    196 
    197 
    198 RFC2217_PARITY_MAP = {
    199     PARITY_NONE: 1,
    200     PARITY_ODD: 2,
    201     PARITY_EVEN: 3,
    202     PARITY_MARK: 4,
    203     PARITY_SPACE: 5,
    204 }
    205 RFC2217_REVERSE_PARITY_MAP = dict((v,k) for k,v in RFC2217_PARITY_MAP.items())
    206 
    207 RFC2217_STOPBIT_MAP = {
    208     STOPBITS_ONE: 1,
    209     STOPBITS_ONE_POINT_FIVE: 3,
    210     STOPBITS_TWO: 2,
    211 }
    212 RFC2217_REVERSE_STOPBIT_MAP = dict((v,k) for k,v in RFC2217_STOPBIT_MAP.items())
    213 
    214 # Telnet filter states
    215 M_NORMAL = 0
    216 M_IAC_SEEN = 1
    217 M_NEGOTIATE = 2
    218 
    219 # TelnetOption and TelnetSubnegotiation states
    220 REQUESTED = 'REQUESTED'
    221 ACTIVE = 'ACTIVE'
    222 INACTIVE = 'INACTIVE'
    223 REALLY_INACTIVE = 'REALLY_INACTIVE'
    224 
    225 class TelnetOption(object):
    226     """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
    227 
    228     def __init__(self, connection, name, option, send_yes, send_no, ack_yes, ack_no, initial_state, activation_callback=None):
    229         """\
    230         Initialize option.
    231         :param connection: connection used to transmit answers
    232         :param name: a readable name for debug outputs
    233         :param send_yes: what to send when option is to be enabled.
    234         :param send_no: what to send when option is to be disabled.
    235         :param ack_yes: what to expect when remote agrees on option.
    236         :param ack_no: what to expect when remote disagrees on option.
    237         :param initial_state: options initialized with REQUESTED are tried to
    238             be enabled on startup. use INACTIVE for all others.
    239         """
    240         self.connection = connection
    241         self.name = name
    242         self.option = option
    243         self.send_yes = send_yes
    244         self.send_no = send_no
    245         self.ack_yes = ack_yes
    246         self.ack_no = ack_no
    247         self.state = initial_state
    248         self.active = False
    249         self.activation_callback = activation_callback
    250 
    251     def __repr__(self):
    252         """String for debug outputs"""
    253         return "%s:%s(%s)" % (self.name, self.active, self.state)
    254 
    255     def process_incoming(self, command):
    256         """A DO/DONT/WILL/WONT was received for this option, update state and
    257         answer when needed."""
    258         if command == self.ack_yes:
    259             if self.state is REQUESTED:
    260                 self.state = ACTIVE
    261                 self.active = True
    262                 if self.activation_callback is not None:
    263                     self.activation_callback()
    264             elif self.state is ACTIVE:
    265                 pass
    266             elif self.state is INACTIVE:
    267                 self.state = ACTIVE
    268                 self.connection.telnetSendOption(self.send_yes, self.option)
    269                 self.active = True
    270                 if self.activation_callback is not None:
    271                     self.activation_callback()
    272             elif self.state is REALLY_INACTIVE:
    273                 self.connection.telnetSendOption(self.send_no, self.option)
    274             else:
    275                 raise ValueError('option in illegal state %r' % self)
    276         elif command == self.ack_no:
    277             if self.state is REQUESTED:
    278                 self.state = INACTIVE
    279                 self.active = False
    280             elif self.state is ACTIVE:
    281                 self.state = INACTIVE
    282                 self.connection.telnetSendOption(self.send_no, self.option)
    283                 self.active = False
    284             elif self.state is INACTIVE:
    285                 pass
    286             elif self.state is REALLY_INACTIVE:
    287                 pass
    288             else:
    289                 raise ValueError('option in illegal state %r' % self)
    290 
    291 
    292 class TelnetSubnegotiation(object):
    293     """\
    294     A object to handle subnegotiation of options. In this case actually
    295     sub-sub options for RFC 2217. It is used to track com port options.
    296     """
    297 
    298     def __init__(self, connection, name, option, ack_option=None):
    299         if ack_option is None: ack_option = option
    300         self.connection = connection
    301         self.name = name
    302         self.option = option
    303         self.value = None
    304         self.ack_option = ack_option
    305         self.state = INACTIVE
    306 
    307     def __repr__(self):
    308         """String for debug outputs."""
    309         return "%s:%s" % (self.name, self.state)
    310 
    311     def set(self, value):
    312         """\
    313         request a change of the value. a request is sent to the server. if
    314         the client needs to know if the change is performed he has to check the
    315         state of this object.
    316         """
    317         self.value = value
    318         self.state = REQUESTED
    319         self.connection.rfc2217SendSubnegotiation(self.option, self.value)
    320         if self.connection.logger:
    321             self.connection.logger.debug("SB Requesting %s -> %r" % (self.name, self.value))
    322 
    323     def isReady(self):
    324         """\
    325         check if answer from server has been received. when server rejects
    326         the change, raise a ValueError.
    327         """
    328         if self.state == REALLY_INACTIVE:
    329             raise ValueError("remote rejected value for option %r" % (self.name))
    330         return self.state == ACTIVE
    331     # add property to have a similar interface as TelnetOption
    332     active = property(isReady)
    333 
    334     def wait(self, timeout=3):
    335         """\
    336         wait until the subnegotiation has been acknowledged or timeout. It
    337         can also throw a value error when the answer from the server does not
    338         match the value sent.
    339         """
    340         timeout_time = time.time() + timeout
    341         while time.time() < timeout_time:
    342             time.sleep(0.05)    # prevent 100% CPU load
    343             if self.isReady():
    344                 break
    345         else:
    346             raise SerialException("timeout while waiting for option %r" % (self.name))
    347 
    348     def checkAnswer(self, suboption):
    349         """\
    350         check an incoming subnegotiation block. the parameter already has
    351         cut off the header like sub option number and com port option value.
    352         """
    353         if self.value == suboption[:len(self.value)]:
    354             self.state = ACTIVE
    355         else:
    356             # error propagation done in isReady
    357             self.state = REALLY_INACTIVE
    358         if self.connection.logger:
    359             self.connection.logger.debug("SB Answer %s -> %r -> %s" % (self.name, suboption, self.state))
    360 
    361 
    362 class RFC2217Serial(SerialBase):
    363     """Serial port implementation for RFC 2217 remote serial ports."""
    364 
    365     BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
    366                  9600, 19200, 38400, 57600, 115200)
    367 
    368     def open(self):
    369         """\
    370         Open port with current settings. This may throw a SerialException
    371         if the port cannot be opened.
    372         """
    373         self.logger = None
    374         self._ignore_set_control_answer = False
    375         self._poll_modem_state = False
    376         self._network_timeout = 3
    377         if self._port is None:
    378             raise SerialException("Port must be configured before it can be used.")
    379         if self._isOpen:
    380             raise SerialException("Port is already open.")
    381         try:
    382             self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    383             self._socket.connect(self.fromURL(self.portstr))
    384             self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
    385         except Exception, msg:
    386             self._socket = None
    387             raise SerialException("Could not open port %s: %s" % (self.portstr, msg))
    388 
    389         self._socket.settimeout(5) # XXX good value?
    390 
    391         # use a thread save queue as buffer. it also simplifies implementing
    392         # the read timeout
    393         self._read_buffer = Queue.Queue()
    394         # to ensure that user writes does not interfere with internal
    395         # telnet/rfc2217 options establish a lock
    396         self._write_lock = threading.Lock()
    397         # name the following separately so that, below, a check can be easily done
    398         mandadory_options = [
    399             TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
    400             TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED),
    401         ]
    402         # all supported telnet options
    403         self._telnet_options = [
    404             TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED),
    405             TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
    406             TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED),
    407             TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE),
    408             TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED),
    409         ] + mandadory_options
    410         # RFC 2217 specific states
    411         # COM port settings
    412         self._rfc2217_port_settings = {
    413             'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE),
    414             'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE),
    415             'parity':   TelnetSubnegotiation(self, 'parity',   SET_PARITY,   SERVER_SET_PARITY),
    416             'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE),
    417             }
    418         # There are more subnegotiation objects, combine all in one dictionary
    419         # for easy access
    420         self._rfc2217_options = {
    421             'purge':    TelnetSubnegotiation(self, 'purge',    PURGE_DATA,   SERVER_PURGE_DATA),
    422             'control':  TelnetSubnegotiation(self, 'control',  SET_CONTROL,  SERVER_SET_CONTROL),
    423             }
    424         self._rfc2217_options.update(self._rfc2217_port_settings)
    425         # cache for line and modem states that the server sends to us
    426         self._linestate = 0
    427         self._modemstate = None
    428         self._modemstate_expires = 0
    429         # RFC 2217 flow control between server and client
    430         self._remote_suspend_flow = False
    431 
    432         self._thread = threading.Thread(target=self._telnetReadLoop)
    433         self._thread.setDaemon(True)
    434         self._thread.setName('pySerial RFC 2217 reader thread for %s' % (self._port,))
    435         self._thread.start()
    436 
    437         # negotiate Telnet/RFC 2217 -> send initial requests
    438         for option in self._telnet_options:
    439             if option.state is REQUESTED:
    440                 self.telnetSendOption(option.send_yes, option.option)
    441         # now wait until important options are negotiated
    442         timeout_time = time.time() + self._network_timeout
    443         while time.time() < timeout_time:
    444             time.sleep(0.05)    # prevent 100% CPU load
    445             if sum(o.active for o in mandadory_options) == len(mandadory_options):
    446                 break
    447         else:
    448             raise SerialException("Remote does not seem to support RFC2217 or BINARY mode %r" % mandadory_options)
    449         if self.logger:
    450             self.logger.info("Negotiated options: %s" % self._telnet_options)
    451 
    452         # fine, go on, set RFC 2271 specific things
    453         self._reconfigurePort()
    454         # all things set up get, now a clean start
    455         self._isOpen = True
    456         if not self._rtscts:
    457             self.setRTS(True)
    458             self.setDTR(True)
    459         self.flushInput()
    460         self.flushOutput()
    461 
    462     def _reconfigurePort(self):
    463         """Set communication parameters on opened port."""
    464         if self._socket is None:
    465             raise SerialException("Can only operate on open ports")
    466 
    467         # if self._timeout != 0 and self._interCharTimeout is not None:
    468             # XXX
    469 
    470         if self._writeTimeout is not None:
    471             raise NotImplementedError('writeTimeout is currently not supported')
    472             # XXX
    473 
    474         # Setup the connection
    475         # to get good performance, all parameter changes are sent first...
    476         if not isinstance(self._baudrate, (int, long)) or not 0 < self._baudrate < 2**32:
    477             raise ValueError("invalid baudrate: %r" % (self._baudrate))
    478         self._rfc2217_port_settings['baudrate'].set(struct.pack('!I', self._baudrate))
    479         self._rfc2217_port_settings['datasize'].set(struct.pack('!B', self._bytesize))
    480         self._rfc2217_port_settings['parity'].set(struct.pack('!B', RFC2217_PARITY_MAP[self._parity]))
    481         self._rfc2217_port_settings['stopsize'].set(struct.pack('!B', RFC2217_STOPBIT_MAP[self._stopbits]))
    482 
    483         # and now wait until parameters are active
    484         items = self._rfc2217_port_settings.values()
    485         if self.logger:
    486             self.logger.debug("Negotiating settings: %s" % (items,))
    487         timeout_time = time.time() + self._network_timeout
    488         while time.time() < timeout_time:
    489             time.sleep(0.05)    # prevent 100% CPU load
    490             if sum(o.active for o in items) == len(items):
    491                 break
    492         else:
    493             raise SerialException("Remote does not accept parameter change (RFC2217): %r" % items)
    494         if self.logger:
    495             self.logger.info("Negotiated settings: %s" % (items,))
    496 
    497         if self._rtscts and self._xonxoff:
    498             raise ValueError('xonxoff and rtscts together are not supported')
    499         elif self._rtscts:
    500             self.rfc2217SetControl(SET_CONTROL_USE_HW_FLOW_CONTROL)
    501         elif self._xonxoff:
    502             self.rfc2217SetControl(SET_CONTROL_USE_SW_FLOW_CONTROL)
    503         else:
    504             self.rfc2217SetControl(SET_CONTROL_USE_NO_FLOW_CONTROL)
    505 
    506     def close(self):
    507         """Close port"""
    508         if self._isOpen:
    509             if self._socket:
    510                 try:
    511                     self._socket.shutdown(socket.SHUT_RDWR)
    512                     self._socket.close()
    513                 except:
    514                     # ignore errors.
    515                     pass
    516                 self._socket = None
    517             if self._thread:
    518                 self._thread.join()
    519             self._isOpen = False
    520             # in case of quick reconnects, give the server some time
    521             time.sleep(0.3)
    522 
    523     def makeDeviceName(self, port):
    524         raise SerialException("there is no sensible way to turn numbers into URLs")
    525 
    526     def fromURL(self, url):
    527         """extract host and port from an URL string"""
    528         if url.lower().startswith("rfc2217://"): url = url[10:]
    529         try:
    530             # is there a "path" (our options)?
    531             if '/' in url:
    532                 # cut away options
    533                 url, options = url.split('/', 1)
    534                 # process options now, directly altering self
    535                 for option in options.split('/'):
    536                     if '=' in option:
    537                         option, value = option.split('=', 1)
    538                     else:
    539                         value = None
    540                     if option == 'logging':
    541                         logging.basicConfig()   # XXX is that good to call it here?
    542                         self.logger = logging.getLogger('pySerial.rfc2217')
    543                         self.logger.setLevel(LOGGER_LEVELS[value])
    544                         self.logger.debug('enabled logging')
    545                     elif option == 'ign_set_control':
    546                         self._ignore_set_control_answer = True
    547                     elif option == 'poll_modem':
    548                         self._poll_modem_state = True
    549                     elif option == 'timeout':
    550                         self._network_timeout = float(value)
    551                     else:
    552                         raise ValueError('unknown option: %r' % (option,))
    553             # get host and port
    554             host, port = url.split(':', 1) # may raise ValueError because of unpacking
    555             port = int(port)               # and this if it's not a number
    556             if not 0 <= port < 65536: raise ValueError("port not in range 0...65535")
    557         except ValueError, e:
    558             raise SerialException('expected a string in the form "[rfc2217://]<host>:<port>[/option[/option...]]": %s' % e)
    559         return (host, port)
    560 
    561     #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    562 
    563     def inWaiting(self):
    564         """Return the number of characters currently in the input buffer."""
    565         if not self._isOpen: raise portNotOpenError
    566         return self._read_buffer.qsize()
    567 
    568     def read(self, size=1):
    569         """\
    570         Read size bytes from the serial port. If a timeout is set it may
    571         return less characters as requested. With no timeout it will block
    572         until the requested number of bytes is read.
    573         """
    574         if not self._isOpen: raise portNotOpenError
    575         data = bytearray()
    576         try:
    577             while len(data) < size:
    578                 if self._thread is None:
    579                     raise SerialException('connection failed (reader thread died)')
    580                 data.append(self._read_buffer.get(True, self._timeout))
    581         except Queue.Empty: # -> timeout
    582             pass
    583         return bytes(data)
    584 
    585     def write(self, data):
    586         """\
    587         Output the given string over the serial port. Can block if the
    588         connection is blocked. May raise SerialException if the connection is
    589         closed.
    590         """
    591         if not self._isOpen: raise portNotOpenError
    592         self._write_lock.acquire()
    593         try:
    594             try:
    595                 self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED))
    596             except socket.error, e:
    597                 raise SerialException("connection failed (socket error): %s" % e) # XXX what exception if socket connection fails
    598         finally:
    599             self._write_lock.release()
    600         return len(data)
    601 
    602     def flushInput(self):
    603         """Clear input buffer, discarding all that is in the buffer."""
    604         if not self._isOpen: raise portNotOpenError
    605         self.rfc2217SendPurge(PURGE_RECEIVE_BUFFER)
    606         # empty read buffer
    607         while self._read_buffer.qsize():
    608             self._read_buffer.get(False)
    609 
    610     def flushOutput(self):
    611         """\
    612         Clear output buffer, aborting the current output and
    613         discarding all that is in the buffer.
    614         """
    615         if not self._isOpen: raise portNotOpenError
    616         self.rfc2217SendPurge(PURGE_TRANSMIT_BUFFER)
    617 
    618     def sendBreak(self, duration=0.25):
    619         """Send break condition. Timed, returns to idle state after given
    620         duration."""
    621         if not self._isOpen: raise portNotOpenError
    622         self.setBreak(True)
    623         time.sleep(duration)
    624         self.setBreak(False)
    625 
    626     def setBreak(self, level=True):
    627         """\
    628         Set break: Controls TXD. When active, to transmitting is
    629         possible.
    630         """
    631         if not self._isOpen: raise portNotOpenError
    632         if self.logger:
    633             self.logger.info('set BREAK to %s' % ('inactive', 'active')[bool(level)])
    634         if level:
    635             self.rfc2217SetControl(SET_CONTROL_BREAK_ON)
    636         else:
    637             self.rfc2217SetControl(SET_CONTROL_BREAK_OFF)
    638 
    639     def setRTS(self, level=True):
    640         """Set terminal status line: Request To Send."""
    641         if not self._isOpen: raise portNotOpenError
    642         if self.logger:
    643             self.logger.info('set RTS to %s' % ('inactive', 'active')[bool(level)])
    644         if level:
    645             self.rfc2217SetControl(SET_CONTROL_RTS_ON)
    646         else:
    647             self.rfc2217SetControl(SET_CONTROL_RTS_OFF)
    648 
    649     def setDTR(self, level=True):
    650         """Set terminal status line: Data Terminal Ready."""
    651         if not self._isOpen: raise portNotOpenError
    652         if self.logger:
    653             self.logger.info('set DTR to %s' % ('inactive', 'active')[bool(level)])
    654         if level:
    655             self.rfc2217SetControl(SET_CONTROL_DTR_ON)
    656         else:
    657             self.rfc2217SetControl(SET_CONTROL_DTR_OFF)
    658 
    659     def getCTS(self):
    660         """Read terminal status line: Clear To Send."""
    661         if not self._isOpen: raise portNotOpenError
    662         return bool(self.getModemState() & MODEMSTATE_MASK_CTS)
    663 
    664     def getDSR(self):
    665         """Read terminal status line: Data Set Ready."""
    666         if not self._isOpen: raise portNotOpenError
    667         return bool(self.getModemState() & MODEMSTATE_MASK_DSR)
    668 
    669     def getRI(self):
    670         """Read terminal status line: Ring Indicator."""
    671         if not self._isOpen: raise portNotOpenError
    672         return bool(self.getModemState() & MODEMSTATE_MASK_RI)
    673 
    674     def getCD(self):
    675         """Read terminal status line: Carrier Detect."""
    676         if not self._isOpen: raise portNotOpenError
    677         return bool(self.getModemState() & MODEMSTATE_MASK_CD)
    678 
    679     # - - - platform specific - - -
    680     # None so far
    681 
    682     # - - - RFC2217 specific - - -
    683 
    684     def _telnetReadLoop(self):
    685         """read loop for the socket."""
    686         mode = M_NORMAL
    687         suboption = None
    688         try:
    689             while self._socket is not None:
    690                 try:
    691                     data = self._socket.recv(1024)
    692                 except socket.timeout:
    693                     # just need to get out of recv form time to time to check if
    694                     # still alive
    695                     continue
    696                 except socket.error, e:
    697                     # connection fails -> terminate loop
    698                     if self.logger:
    699                         self.logger.debug("socket error in reader thread: %s" % (e,))
    700                     break
    701                 if not data: break # lost connection
    702                 for byte in data:
    703                     if mode == M_NORMAL:
    704                         # interpret as command or as data
    705                         if byte == IAC:
    706                             mode = M_IAC_SEEN
    707                         else:
    708                             # store data in read buffer or sub option buffer
    709                             # depending on state
    710                             if suboption is not None:
    711                                 suboption.append(byte)
    712                             else:
    713                                 self._read_buffer.put(byte)
    714                     elif mode == M_IAC_SEEN:
    715                         if byte == IAC:
    716                             # interpret as command doubled -> insert character
    717                             # itself
    718                             if suboption is not None:
    719                                 suboption.append(IAC)
    720                             else:
    721                                 self._read_buffer.put(IAC)
    722                             mode = M_NORMAL
    723                         elif byte == SB:
    724                             # sub option start
    725                             suboption = bytearray()
    726                             mode = M_NORMAL
    727                         elif byte == SE:
    728                             # sub option end -> process it now
    729                             self._telnetProcessSubnegotiation(bytes(suboption))
    730                             suboption = None
    731                             mode = M_NORMAL
    732                         elif byte in (DO, DONT, WILL, WONT):
    733                             # negotiation
    734                             telnet_command = byte
    735                             mode = M_NEGOTIATE
    736                         else:
    737                             # other telnet commands
    738                             self._telnetProcessCommand(byte)
    739                             mode = M_NORMAL
    740                     elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
    741                         self._telnetNegotiateOption(telnet_command, byte)
    742                         mode = M_NORMAL
    743         finally:
    744             self._thread = None
    745             if self.logger:
    746                 self.logger.debug("read thread terminated")
    747 
    748     # - incoming telnet commands and options
    749 
    750     def _telnetProcessCommand(self, command):
    751         """Process commands other than DO, DONT, WILL, WONT."""
    752         # Currently none. RFC2217 only uses negotiation and subnegotiation.
    753         if self.logger:
    754             self.logger.warning("ignoring Telnet command: %r" % (command,))
    755 
    756     def _telnetNegotiateOption(self, command, option):
    757         """Process incoming DO, DONT, WILL, WONT."""
    758         # check our registered telnet options and forward command to them
    759         # they know themselves if they have to answer or not
    760         known = False
    761         for item in self._telnet_options:
    762             # can have more than one match! as some options are duplicated for
    763             # 'us' and 'them'
    764             if item.option == option:
    765                 item.process_incoming(command)
    766                 known = True
    767         if not known:
    768             # handle unknown options
    769             # only answer to positive requests and deny them
    770             if command == WILL or command == DO:
    771                 self.telnetSendOption((command == WILL and DONT or WONT), option)
    772                 if self.logger:
    773                     self.logger.warning("rejected Telnet option: %r" % (option,))
    774 
    775 
    776     def _telnetProcessSubnegotiation(self, suboption):
    777         """Process subnegotiation, the data between IAC SB and IAC SE."""
    778         if suboption[0:1] == COM_PORT_OPTION:
    779             if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3:
    780                 self._linestate = ord(suboption[2:3]) # ensure it is a number
    781                 if self.logger:
    782                     self.logger.info("NOTIFY_LINESTATE: %s" % self._linestate)
    783             elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3:
    784                 self._modemstate = ord(suboption[2:3]) # ensure it is a number
    785                 if self.logger:
    786                     self.logger.info("NOTIFY_MODEMSTATE: %s" % self._modemstate)
    787                 # update time when we think that a poll would make sense
    788                 self._modemstate_expires = time.time() + 0.3
    789             elif suboption[1:2] == FLOWCONTROL_SUSPEND:
    790                 self._remote_suspend_flow = True
    791             elif suboption[1:2] == FLOWCONTROL_RESUME:
    792                 self._remote_suspend_flow = False
    793             else:
    794                 for item in self._rfc2217_options.values():
    795                     if item.ack_option == suboption[1:2]:
    796                         #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
    797                         item.checkAnswer(bytes(suboption[2:]))
    798                         break
    799                 else:
    800                     if self.logger:
    801                         self.logger.warning("ignoring COM_PORT_OPTION: %r" % (suboption,))
    802         else:
    803             if self.logger:
    804                 self.logger.warning("ignoring subnegotiation: %r" % (suboption,))
    805 
    806     # - outgoing telnet commands and options
    807 
    808     def _internal_raw_write(self, data):
    809         """internal socket write with no data escaping. used to send telnet stuff."""
    810         self._write_lock.acquire()
    811         try:
    812             self._socket.sendall(data)
    813         finally:
    814             self._write_lock.release()
    815 
    816     def telnetSendOption(self, action, option):
    817         """Send DO, DONT, WILL, WONT."""
    818         self._internal_raw_write(to_bytes([IAC, action, option]))
    819 
    820     def rfc2217SendSubnegotiation(self, option, value=''):
    821         """Subnegotiation of RFC2217 parameters."""
    822         value = value.replace(IAC, IAC_DOUBLED)
    823         self._internal_raw_write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
    824 
    825     def rfc2217SendPurge(self, value):
    826         item = self._rfc2217_options['purge']
    827         item.set(value) # transmit desired purge type
    828         item.wait(self._network_timeout) # wait for acknowledge from the server
    829 
    830     def rfc2217SetControl(self, value):
    831         item = self._rfc2217_options['control']
    832         item.set(value) # transmit desired control type
    833         if self._ignore_set_control_answer:
    834             # answers are ignored when option is set. compatibility mode for
    835             # servers that answer, but not the expected one... (or no answer
    836             # at all) i.e. sredird
    837             time.sleep(0.1)  # this helps getting the unit tests passed
    838         else:
    839             item.wait(self._network_timeout)  # wait for acknowledge from the server
    840 
    841     def rfc2217FlowServerReady(self):
    842         """\
    843         check if server is ready to receive data. block for some time when
    844         not.
    845         """
    846         #~ if self._remote_suspend_flow:
    847             #~ wait---
    848 
    849     def getModemState(self):
    850         """\
    851         get last modem state (cached value. if value is "old", request a new
    852         one. this cache helps that we don't issue to many requests when e.g. all
    853         status lines, one after the other is queried by te user (getCTS, getDSR
    854         etc.)
    855         """
    856         # active modem state polling enabled? is the value fresh enough?
    857         if self._poll_modem_state and self._modemstate_expires < time.time():
    858             if self.logger:
    859                 self.logger.debug('polling modem state')
    860             # when it is older, request an update
    861             self.rfc2217SendSubnegotiation(NOTIFY_MODEMSTATE)
    862             timeout_time = time.time() + self._network_timeout
    863             while time.time() < timeout_time:
    864                 time.sleep(0.05)    # prevent 100% CPU load
    865                 # when expiration time is updated, it means that there is a new
    866                 # value
    867                 if self._modemstate_expires > time.time():
    868                     if self.logger:
    869                         self.logger.warning('poll for modem state failed')
    870                     break
    871             # even when there is a timeout, do not generate an error just
    872             # return the last known value. this way we can support buggy
    873             # servers that do not respond to polls, but send automatic
    874             # updates.
    875         if self._modemstate is not None:
    876             if self.logger:
    877                 self.logger.debug('using cached modem state')
    878             return self._modemstate
    879         else:
    880             # never received a notification from the server
    881             raise SerialException("remote sends no NOTIFY_MODEMSTATE")
    882 
    883 
    884 # assemble Serial class with the platform specific implementation and the base
    885 # for file-like behavior. for Python 2.6 and newer, that provide the new I/O
    886 # library, derive from io.RawIOBase
    887 try:
    888     import io
    889 except ImportError:
    890     # classic version with our own file-like emulation
    891     class Serial(RFC2217Serial, FileLike):
    892         pass
    893 else:
    894     # io library present
    895     class Serial(RFC2217Serial, io.RawIOBase):
    896         pass
    897 
    898 
    899 #############################################################################
    900 # The following is code that helps implementing an RFC 2217 server.
    901 
    902 class PortManager(object):
    903     """\
    904     This class manages the state of Telnet and RFC 2217. It needs a serial
    905     instance and a connection to work with. Connection is expected to implement
    906     a (thread safe) write function, that writes the string to the network.
    907     """
    908 
    909     def __init__(self, serial_port, connection, logger=None):
    910         self.serial = serial_port
    911         self.connection = connection
    912         self.logger = logger
    913         self._client_is_rfc2217 = False
    914 
    915         # filter state machine
    916         self.mode = M_NORMAL
    917         self.suboption = None
    918         self.telnet_command = None
    919 
    920         # states for modem/line control events
    921         self.modemstate_mask = 255
    922         self.last_modemstate = None
    923         self.linstate_mask = 0
    924 
    925         # all supported telnet options
    926         self._telnet_options = [
    927             TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
    928             TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
    929             TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
    930             TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
    931             TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED),
    932             TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok),
    933             TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok),
    934             ]
    935 
    936         # negotiate Telnet/RFC2217 -> send initial requests
    937         if self.logger:
    938             self.logger.debug("requesting initial Telnet/RFC 2217 options")
    939         for option in self._telnet_options:
    940             if option.state is REQUESTED:
    941                 self.telnetSendOption(option.send_yes, option.option)
    942         # issue 1st modem state notification
    943 
    944     def _client_ok(self):
    945         """\
    946         callback of telnet option. it gets called when option is activated.
    947         this one here is used to detect when the client agrees on RFC 2217. a
    948         flag is set so that other functions like check_modem_lines know if the
    949         client is ok.
    950         """
    951         # The callback is used for we and they so if one party agrees, we're
    952         # already happy. it seems not all servers do the negotiation correctly
    953         # and i guess there are incorrect clients too.. so be happy if client
    954         # answers one or the other positively.
    955         self._client_is_rfc2217 = True
    956         if self.logger:
    957             self.logger.info("client accepts RFC 2217")
    958         # this is to ensure that the client gets a notification, even if there
    959         # was no change
    960         self.check_modem_lines(force_notification=True)
    961 
    962     # - outgoing telnet commands and options
    963 
    964     def telnetSendOption(self, action, option):
    965         """Send DO, DONT, WILL, WONT."""
    966         self.connection.write(to_bytes([IAC, action, option]))
    967 
    968     def rfc2217SendSubnegotiation(self, option, value=''):
    969         """Subnegotiation of RFC 2217 parameters."""
    970         value = value.replace(IAC, IAC_DOUBLED)
    971         self.connection.write(to_bytes([IAC, SB, COM_PORT_OPTION, option] + list(value) + [IAC, SE]))
    972 
    973     # - check modem lines, needs to be called periodically from user to
    974     # establish polling
    975 
    976     def check_modem_lines(self, force_notification=False):
    977         modemstate = (
    978             (self.serial.getCTS() and MODEMSTATE_MASK_CTS) |
    979             (self.serial.getDSR() and MODEMSTATE_MASK_DSR) |
    980             (self.serial.getRI() and MODEMSTATE_MASK_RI) |
    981             (self.serial.getCD() and MODEMSTATE_MASK_CD)
    982         )
    983         # check what has changed
    984         deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0
    985         if deltas & MODEMSTATE_MASK_CTS:
    986             modemstate |= MODEMSTATE_MASK_CTS_CHANGE
    987         if deltas & MODEMSTATE_MASK_DSR:
    988             modemstate |= MODEMSTATE_MASK_DSR_CHANGE
    989         if deltas & MODEMSTATE_MASK_RI:
    990             modemstate |= MODEMSTATE_MASK_RI_CHANGE
    991         if deltas & MODEMSTATE_MASK_CD:
    992             modemstate |= MODEMSTATE_MASK_CD_CHANGE
    993         # if new state is different and the mask allows this change, send
    994         # notification. suppress notifications when client is not rfc2217
    995         if modemstate != self.last_modemstate or force_notification:
    996             if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification:
    997                 self.rfc2217SendSubnegotiation(
    998                     SERVER_NOTIFY_MODEMSTATE,
    999                     to_bytes([modemstate & self.modemstate_mask])
   1000                     )
   1001                 if self.logger:
   1002                     self.logger.info("NOTIFY_MODEMSTATE: %s" % (modemstate,))
   1003             # save last state, but forget about deltas.
   1004             # otherwise it would also notify about changing deltas which is
   1005             # probably not very useful
   1006             self.last_modemstate = modemstate & 0xf0
   1007 
   1008     # - outgoing data escaping
   1009 
   1010     def escape(self, data):
   1011         """\
   1012         this generator function is for the user. all outgoing data has to be
   1013         properly escaped, so that no IAC character in the data stream messes up
   1014         the Telnet state machine in the server.
   1015 
   1016         socket.sendall(escape(data))
   1017         """
   1018         for byte in data:
   1019             if byte == IAC:
   1020                 yield IAC
   1021                 yield IAC
   1022             else:
   1023                 yield byte
   1024 
   1025     # - incoming data filter
   1026 
   1027     def filter(self, data):
   1028         """\
   1029         handle a bunch of incoming bytes. this is a generator. it will yield
   1030         all characters not of interest for Telnet/RFC 2217.
   1031 
   1032         The idea is that the reader thread pushes data from the socket through
   1033         this filter:
   1034 
   1035         for byte in filter(socket.recv(1024)):
   1036             # do things like CR/LF conversion/whatever
   1037             # and write data to the serial port
   1038             serial.write(byte)
   1039 
   1040         (socket error handling code left as exercise for the reader)
   1041         """
   1042         for byte in data:
   1043             if self.mode == M_NORMAL:
   1044                 # interpret as command or as data
   1045                 if byte == IAC:
   1046                     self.mode = M_IAC_SEEN
   1047                 else:
   1048                     # store data in sub option buffer or pass it to our
   1049                     # consumer depending on state
   1050                     if self.suboption is not None:
   1051                         self.suboption.append(byte)
   1052                     else:
   1053                         yield byte
   1054             elif self.mode == M_IAC_SEEN:
   1055                 if byte == IAC:
   1056                     # interpret as command doubled -> insert character
   1057                     # itself
   1058                     if self.suboption is not None:
   1059                         self.suboption.append(byte)
   1060                     else:
   1061                         yield byte
   1062                     self.mode = M_NORMAL
   1063                 elif byte == SB:
   1064                     # sub option start
   1065                     self.suboption = bytearray()
   1066                     self.mode = M_NORMAL
   1067                 elif byte == SE:
   1068                     # sub option end -> process it now
   1069                     self._telnetProcessSubnegotiation(bytes(self.suboption))
   1070                     self.suboption = None
   1071                     self.mode = M_NORMAL
   1072                 elif byte in (DO, DONT, WILL, WONT):
   1073                     # negotiation
   1074                     self.telnet_command = byte
   1075                     self.mode = M_NEGOTIATE
   1076                 else:
   1077                     # other telnet commands
   1078                     self._telnetProcessCommand(byte)
   1079                     self.mode = M_NORMAL
   1080             elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
   1081                 self._telnetNegotiateOption(self.telnet_command, byte)
   1082                 self.mode = M_NORMAL
   1083 
   1084     # - incoming telnet commands and options
   1085 
   1086     def _telnetProcessCommand(self, command):
   1087         """Process commands other than DO, DONT, WILL, WONT."""
   1088         # Currently none. RFC2217 only uses negotiation and subnegotiation.
   1089         if self.logger:
   1090             self.logger.warning("ignoring Telnet command: %r" % (command,))
   1091 
   1092     def _telnetNegotiateOption(self, command, option):
   1093         """Process incoming DO, DONT, WILL, WONT."""
   1094         # check our registered telnet options and forward command to them
   1095         # they know themselves if they have to answer or not
   1096         known = False
   1097         for item in self._telnet_options:
   1098             # can have more than one match! as some options are duplicated for
   1099             # 'us' and 'them'
   1100             if item.option == option:
   1101                 item.process_incoming(command)
   1102                 known = True
   1103         if not known:
   1104             # handle unknown options
   1105             # only answer to positive requests and deny them
   1106             if command == WILL or command == DO:
   1107                 self.telnetSendOption((command == WILL and DONT or WONT), option)
   1108                 if self.logger:
   1109                     self.logger.warning("rejected Telnet option: %r" % (option,))
   1110 
   1111 
   1112     def _telnetProcessSubnegotiation(self, suboption):
   1113         """Process subnegotiation, the data between IAC SB and IAC SE."""
   1114         if suboption[0:1] == COM_PORT_OPTION:
   1115             if self.logger:
   1116                 self.logger.debug('received COM_PORT_OPTION: %r' % (suboption,))
   1117             if suboption[1:2] == SET_BAUDRATE:
   1118                 backup = self.serial.baudrate
   1119                 try:
   1120                     (baudrate,) = struct.unpack("!I", suboption[2:6])
   1121                     if baudrate != 0:
   1122                         self.serial.baudrate = baudrate
   1123                 except ValueError, e:
   1124                     if self.logger:
   1125                         self.logger.error("failed to set baud rate: %s" % (e,))
   1126                     self.serial.baudrate = backup
   1127                 else:
   1128                     if self.logger:
   1129                         self.logger.info("%s baud rate: %s" % (baudrate and 'set' or 'get', self.serial.baudrate))
   1130                 self.rfc2217SendSubnegotiation(SERVER_SET_BAUDRATE, struct.pack("!I", self.serial.baudrate))
   1131             elif suboption[1:2] == SET_DATASIZE:
   1132                 backup = self.serial.bytesize
   1133                 try:
   1134                     (datasize,) = struct.unpack("!B", suboption[2:3])
   1135                     if datasize != 0:
   1136                         self.serial.bytesize = datasize
   1137                 except ValueError, e:
   1138                     if self.logger:
   1139                         self.logger.error("failed to set data size: %s" % (e,))
   1140                     self.serial.bytesize = backup
   1141                 else:
   1142                     if self.logger:
   1143                         self.logger.info("%s data size: %s" % (datasize and 'set' or 'get', self.serial.bytesize))
   1144                 self.rfc2217SendSubnegotiation(SERVER_SET_DATASIZE, struct.pack("!B", self.serial.bytesize))
   1145             elif suboption[1:2] == SET_PARITY:
   1146                 backup = self.serial.parity
   1147                 try:
   1148                     parity = struct.unpack("!B", suboption[2:3])[0]
   1149                     if parity != 0:
   1150                             self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity]
   1151                 except ValueError, e:
   1152                     if self.logger:
   1153                         self.logger.error("failed to set parity: %s" % (e,))
   1154                     self.serial.parity = backup
   1155                 else:
   1156                     if self.logger:
   1157                         self.logger.info("%s parity: %s" % (parity and 'set' or 'get', self.serial.parity))
   1158                 self.rfc2217SendSubnegotiation(
   1159                     SERVER_SET_PARITY,
   1160                     struct.pack("!B", RFC2217_PARITY_MAP[self.serial.parity])
   1161                     )
   1162             elif suboption[1:2] == SET_STOPSIZE:
   1163                 backup = self.serial.stopbits
   1164                 try:
   1165                     stopbits = struct.unpack("!B", suboption[2:3])[0]
   1166                     if stopbits != 0:
   1167                         self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits]
   1168                 except ValueError, e:
   1169                     if self.logger:
   1170                         self.logger.error("failed to set stop bits: %s" % (e,))
   1171                     self.serial.stopbits = backup
   1172                 else:
   1173                     if self.logger:
   1174                         self.logger.info("%s stop bits: %s" % (stopbits and 'set' or 'get', self.serial.stopbits))
   1175                 self.rfc2217SendSubnegotiation(
   1176                     SERVER_SET_STOPSIZE,
   1177                     struct.pack("!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])
   1178                     )
   1179             elif suboption[1:2] == SET_CONTROL:
   1180                 if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING:
   1181                     if self.serial.xonxoff:
   1182                         self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
   1183                     elif self.serial.rtscts:
   1184                         self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
   1185                     else:
   1186                         self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
   1187                 elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL:
   1188                     self.serial.xonxoff = False
   1189                     self.serial.rtscts = False
   1190                     if self.logger:
   1191                         self.logger.info("changed flow control to None")
   1192                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
   1193                 elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL:
   1194                     self.serial.xonxoff = True
   1195                     if self.logger:
   1196                         self.logger.info("changed flow control to XON/XOFF")
   1197                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
   1198                 elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL:
   1199                     self.serial.rtscts = True
   1200                     if self.logger:
   1201                         self.logger.info("changed flow control to RTS/CTS")
   1202                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
   1203                 elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE:
   1204                     if self.logger:
   1205                         self.logger.warning("requested break state - not implemented")
   1206                     pass # XXX needs cached value
   1207                 elif suboption[2:3] == SET_CONTROL_BREAK_ON:
   1208                     self.serial.setBreak(True)
   1209                     if self.logger:
   1210                         self.logger.info("changed BREAK to active")
   1211                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON)
   1212                 elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
   1213                     self.serial.setBreak(False)
   1214                     if self.logger:
   1215                         self.logger.info("changed BREAK to inactive")
   1216                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF)
   1217                 elif suboption[2:3] == SET_CONTROL_REQ_DTR:
   1218                     if self.logger:
   1219                         self.logger.warning("requested DTR state - not implemented")
   1220                     pass # XXX needs cached value
   1221                 elif suboption[2:3] == SET_CONTROL_DTR_ON:
   1222                     self.serial.setDTR(True)
   1223                     if self.logger:
   1224                         self.logger.info("changed DTR to active")
   1225                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON)
   1226                 elif suboption[2:3] == SET_CONTROL_DTR_OFF:
   1227                     self.serial.setDTR(False)
   1228                     if self.logger:
   1229                         self.logger.info("changed DTR to inactive")
   1230                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF)
   1231                 elif suboption[2:3] == SET_CONTROL_REQ_RTS:
   1232                     if self.logger:
   1233                         self.logger.warning("requested RTS state - not implemented")
   1234                     pass # XXX needs cached value
   1235                     #~ self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
   1236                 elif suboption[2:3] == SET_CONTROL_RTS_ON:
   1237                     self.serial.setRTS(True)
   1238                     if self.logger:
   1239                         self.logger.info("changed RTS to active")
   1240                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
   1241                 elif suboption[2:3] == SET_CONTROL_RTS_OFF:
   1242                     self.serial.setRTS(False)
   1243                     if self.logger:
   1244                         self.logger.info("changed RTS to inactive")
   1245                     self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF)
   1246                 #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
   1247                 #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
   1248                 #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
   1249                 #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
   1250                 #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
   1251                 #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
   1252                 #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
   1253             elif suboption[1:2] == NOTIFY_LINESTATE:
   1254                 # client polls for current state
   1255                 self.rfc2217SendSubnegotiation(
   1256                     SERVER_NOTIFY_LINESTATE,
   1257                     to_bytes([0])   # sorry, nothing like that implemented
   1258                     )
   1259             elif suboption[1:2] == NOTIFY_MODEMSTATE:
   1260                 if self.logger:
   1261                     self.logger.info("request for modem state")
   1262                 # client polls for current state
   1263                 self.check_modem_lines(force_notification=True)
   1264             elif suboption[1:2] == FLOWCONTROL_SUSPEND:
   1265                 if self.logger:
   1266                     self.logger.info("suspend")
   1267                 self._remote_suspend_flow = True
   1268             elif suboption[1:2] == FLOWCONTROL_RESUME:
   1269                 if self.logger:
   1270                     self.logger.info("resume")
   1271                 self._remote_suspend_flow = False
   1272             elif suboption[1:2] == SET_LINESTATE_MASK:
   1273                 self.linstate_mask = ord(suboption[2:3]) # ensure it is a number
   1274                 if self.logger:
   1275                     self.logger.info("line state mask: 0x%02x" % (self.linstate_mask,))
   1276             elif suboption[1:2] == SET_MODEMSTATE_MASK:
   1277                 self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number
   1278                 if self.logger:
   1279                     self.logger.info("modem state mask: 0x%02x" % (self.modemstate_mask,))
   1280             elif suboption[1:2] == PURGE_DATA:
   1281                 if suboption[2:3] == PURGE_RECEIVE_BUFFER:
   1282                     self.serial.flushInput()
   1283                     if self.logger:
   1284                         self.logger.info("purge in")
   1285                     self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER)
   1286                 elif suboption[2:3] == PURGE_TRANSMIT_BUFFER:
   1287                     self.serial.flushOutput()
   1288                     if self.logger:
   1289                         self.logger.info("purge out")
   1290                     self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER)
   1291                 elif suboption[2:3] == PURGE_BOTH_BUFFERS:
   1292                     self.serial.flushInput()
   1293                     self.serial.flushOutput()
   1294                     if self.logger:
   1295                         self.logger.info("purge both")
   1296                     self.rfc2217SendSubnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS)
   1297                 else:
   1298                     if self.logger:
   1299                         self.logger.error("undefined PURGE_DATA: %r" % list(suboption[2:]))
   1300             else:
   1301                 if self.logger:
   1302                     self.logger.error("undefined COM_PORT_OPTION: %r" % list(suboption[1:]))
   1303         else:
   1304             if self.logger:
   1305                 self.logger.warning("unknown subnegotiation: %r" % (suboption,))
   1306 
   1307 
   1308 # simple client test
   1309 if __name__ == '__main__':
   1310     import sys
   1311     s = Serial('rfc2217://localhost:7000', 115200)
   1312     sys.stdout.write('%s\n' % s)
   1313 
   1314     #~ s.baudrate = 1898
   1315 
   1316     sys.stdout.write("write...\n")
   1317     s.write("hello\n")
   1318     s.flush()
   1319     sys.stdout.write("read: %s\n" % s.read(5))
   1320 
   1321     #~ s.baudrate = 19200
   1322     #~ s.databits = 7
   1323     s.close()
   1324