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@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
62from serial.serialutil import *
63import time
64import struct
65import socket
66import threading
67import Queue
68import 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()
76LOGGER_LEVELS = {
77    'debug': logging.DEBUG,
78    'info': logging.INFO,
79    'warning': logging.WARNING,
80    'error': logging.ERROR,
81    }
82
83
84# telnet protocol characters
85IAC  = to_bytes([255]) # Interpret As Command
86DONT = to_bytes([254])
87DO   = to_bytes([253])
88WONT = to_bytes([252])
89WILL = to_bytes([251])
90IAC_DOUBLED = to_bytes([IAC, IAC])
91
92SE  = to_bytes([240])  # Subnegotiation End
93NOP = to_bytes([241])  # No Operation
94DM  = to_bytes([242])  # Data Mark
95BRK = to_bytes([243])  # Break
96IP  = to_bytes([244])  # Interrupt process
97AO  = to_bytes([245])  # Abort output
98AYT = to_bytes([246])  # Are You There
99EC  = to_bytes([247])  # Erase Character
100EL  = to_bytes([248])  # Erase Line
101GA  = to_bytes([249])  # Go Ahead
102SB =  to_bytes([250])  # Subnegotiation Begin
103
104# selected telnet options
105BINARY = to_bytes([0]) # 8-bit data path
106ECHO = to_bytes([1])   # echo
107SGA = to_bytes([3])    # suppress go ahead
108
109# RFC2217
110COM_PORT_OPTION = to_bytes([44])
111
112# Client to Access Server
113SET_BAUDRATE = to_bytes([1])
114SET_DATASIZE = to_bytes([2])
115SET_PARITY = to_bytes([3])
116SET_STOPSIZE = to_bytes([4])
117SET_CONTROL = to_bytes([5])
118NOTIFY_LINESTATE = to_bytes([6])
119NOTIFY_MODEMSTATE = to_bytes([7])
120FLOWCONTROL_SUSPEND = to_bytes([8])
121FLOWCONTROL_RESUME = to_bytes([9])
122SET_LINESTATE_MASK = to_bytes([10])
123SET_MODEMSTATE_MASK = to_bytes([11])
124PURGE_DATA = to_bytes([12])
125
126SERVER_SET_BAUDRATE = to_bytes([101])
127SERVER_SET_DATASIZE = to_bytes([102])
128SERVER_SET_PARITY = to_bytes([103])
129SERVER_SET_STOPSIZE = to_bytes([104])
130SERVER_SET_CONTROL = to_bytes([105])
131SERVER_NOTIFY_LINESTATE = to_bytes([106])
132SERVER_NOTIFY_MODEMSTATE = to_bytes([107])
133SERVER_FLOWCONTROL_SUSPEND = to_bytes([108])
134SERVER_FLOWCONTROL_RESUME = to_bytes([109])
135SERVER_SET_LINESTATE_MASK = to_bytes([110])
136SERVER_SET_MODEMSTATE_MASK = to_bytes([111])
137SERVER_PURGE_DATA = to_bytes([112])
138
139RFC2217_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
154SET_CONTROL_REQ_FLOW_SETTING = to_bytes([0])        # Request Com Port Flow Control Setting (outbound/both)
155SET_CONTROL_USE_NO_FLOW_CONTROL = to_bytes([1])     # Use No Flow Control (outbound/both)
156SET_CONTROL_USE_SW_FLOW_CONTROL = to_bytes([2])     # Use XON/XOFF Flow Control (outbound/both)
157SET_CONTROL_USE_HW_FLOW_CONTROL = to_bytes([3])     # Use HARDWARE Flow Control (outbound/both)
158SET_CONTROL_REQ_BREAK_STATE = to_bytes([4])         # Request BREAK State
159SET_CONTROL_BREAK_ON = to_bytes([5])                # Set BREAK State ON
160SET_CONTROL_BREAK_OFF = to_bytes([6])               # Set BREAK State OFF
161SET_CONTROL_REQ_DTR = to_bytes([7])                 # Request DTR Signal State
162SET_CONTROL_DTR_ON = to_bytes([8])                  # Set DTR Signal State ON
163SET_CONTROL_DTR_OFF = to_bytes([9])                 # Set DTR Signal State OFF
164SET_CONTROL_REQ_RTS = to_bytes([10])                # Request RTS Signal State
165SET_CONTROL_RTS_ON = to_bytes([11])                 # Set RTS Signal State ON
166SET_CONTROL_RTS_OFF = to_bytes([12])                # Set RTS Signal State OFF
167SET_CONTROL_REQ_FLOW_SETTING_IN = to_bytes([13])    # Request Com Port Flow Control Setting (inbound)
168SET_CONTROL_USE_NO_FLOW_CONTROL_IN = to_bytes([14]) # Use No Flow Control (inbound)
169SET_CONTROL_USE_SW_FLOW_CONTOL_IN = to_bytes([15])  # Use XON/XOFF Flow Control (inbound)
170SET_CONTROL_USE_HW_FLOW_CONTOL_IN = to_bytes([16])  # Use HARDWARE Flow Control (inbound)
171SET_CONTROL_USE_DCD_FLOW_CONTROL = to_bytes([17])   # Use DCD Flow Control (outbound/both)
172SET_CONTROL_USE_DTR_FLOW_CONTROL = to_bytes([18])   # Use DTR Flow Control (inbound)
173SET_CONTROL_USE_DSR_FLOW_CONTROL = to_bytes([19])   # Use DSR Flow Control (outbound/both)
174
175LINESTATE_MASK_TIMEOUT = 128                # Time-out Error
176LINESTATE_MASK_SHIFTREG_EMPTY = 64          # Transfer Shift Register Empty
177LINESTATE_MASK_TRANSREG_EMPTY = 32          # Transfer Holding Register Empty
178LINESTATE_MASK_BREAK_DETECT = 16            # Break-detect Error
179LINESTATE_MASK_FRAMING_ERROR = 8            # Framing Error
180LINESTATE_MASK_PARTIY_ERROR = 4             # Parity Error
181LINESTATE_MASK_OVERRUN_ERROR = 2            # Overrun Error
182LINESTATE_MASK_DATA_READY = 1               # Data Ready
183
184MODEMSTATE_MASK_CD = 128                    # Receive Line Signal Detect (also known as Carrier Detect)
185MODEMSTATE_MASK_RI = 64                     # Ring Indicator
186MODEMSTATE_MASK_DSR = 32                    # Data-Set-Ready Signal State
187MODEMSTATE_MASK_CTS = 16                    # Clear-To-Send Signal State
188MODEMSTATE_MASK_CD_CHANGE = 8               # Delta Receive Line Signal Detect
189MODEMSTATE_MASK_RI_CHANGE = 4               # Trailing-edge Ring Detector
190MODEMSTATE_MASK_DSR_CHANGE = 2              # Delta Data-Set-Ready
191MODEMSTATE_MASK_CTS_CHANGE = 1              # Delta Clear-To-Send
192
193PURGE_RECEIVE_BUFFER = to_bytes([1])        # Purge access server receive data buffer
194PURGE_TRANSMIT_BUFFER = to_bytes([2])       # Purge access server transmit data buffer
195PURGE_BOTH_BUFFERS = to_bytes([3])          # Purge both the access server receive data buffer and the access server transmit data buffer
196
197
198RFC2217_PARITY_MAP = {
199    PARITY_NONE: 1,
200    PARITY_ODD: 2,
201    PARITY_EVEN: 3,
202    PARITY_MARK: 4,
203    PARITY_SPACE: 5,
204}
205RFC2217_REVERSE_PARITY_MAP = dict((v,k) for k,v in RFC2217_PARITY_MAP.items())
206
207RFC2217_STOPBIT_MAP = {
208    STOPBITS_ONE: 1,
209    STOPBITS_ONE_POINT_FIVE: 3,
210    STOPBITS_TWO: 2,
211}
212RFC2217_REVERSE_STOPBIT_MAP = dict((v,k) for k,v in RFC2217_STOPBIT_MAP.items())
213
214# Telnet filter states
215M_NORMAL = 0
216M_IAC_SEEN = 1
217M_NEGOTIATE = 2
218
219# TelnetOption and TelnetSubnegotiation states
220REQUESTED = 'REQUESTED'
221ACTIVE = 'ACTIVE'
222INACTIVE = 'INACTIVE'
223REALLY_INACTIVE = 'REALLY_INACTIVE'
224
225class 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
292class 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
362class 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
887try:
888    import io
889except ImportError:
890    # classic version with our own file-like emulation
891    class Serial(RFC2217Serial, FileLike):
892        pass
893else:
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
902class 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
1309if __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