Update sdk/platform-tools to version 26.0.0.
[android_tools.git] / sdk / platform-tools / systrace / catapult / telemetry / third_party / pyserial / serial / rfc2217.py
blob2012ea71c11667f3d93fb6ea1b634a8553b8c56b
1 #! python
3 # Python Serial Port Extension for Win32, Linux, BSD, Jython
4 # see __init__.py
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.
10 # (C) 2001-2013 Chris Liechti <cliechti@gmx.net>
11 # this is distributed under a free software license, see license.txt
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
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 ##############################################################################
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...]]
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
62 from serial.serialutil import *
63 import time
64 import struct
65 import socket
66 import threading
67 import Queue
68 import logging
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
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,
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])
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
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
109 # RFC2217
110 COM_PORT_OPTION = to_bytes([44])
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])
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])
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,
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)
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
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
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
198 RFC2217_PARITY_MAP = {
199 PARITY_NONE: 1,
200 PARITY_ODD: 2,
201 PARITY_EVEN: 3,
202 PARITY_MARK: 4,
203 PARITY_SPACE: 5,
205 RFC2217_REVERSE_PARITY_MAP = dict((v,k) for k,v in RFC2217_PARITY_MAP.items())
207 RFC2217_STOPBIT_MAP = {
208 STOPBITS_ONE: 1,
209 STOPBITS_ONE_POINT_FIVE: 3,
210 STOPBITS_TWO: 2,
212 RFC2217_REVERSE_STOPBIT_MAP = dict((v,k) for k,v in RFC2217_STOPBIT_MAP.items())
214 # Telnet filter states
215 M_NORMAL = 0
216 M_IAC_SEEN = 1
217 M_NEGOTIATE = 2
219 # TelnetOption and TelnetSubnegotiation states
220 REQUESTED = 'REQUESTED'
221 ACTIVE = 'ACTIVE'
222 INACTIVE = 'INACTIVE'
223 REALLY_INACTIVE = 'REALLY_INACTIVE'
225 class TelnetOption(object):
226 """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
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.
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
251 def __repr__(self):
252 """String for debug outputs"""
253 return "%s:%s(%s)" % (self.name, self.active, self.state)
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)
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.
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
307 def __repr__(self):
308 """String for debug outputs."""
309 return "%s:%s" % (self.name, self.state)
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.
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))
323 def isReady(self):
324 """\
325 check if answer from server has been received. when server rejects
326 the change, raise a ValueError.
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)
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.
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))
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.
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))
362 class RFC2217Serial(SerialBase):
363 """Serial port implementation for RFC 2217 remote serial ports."""
365 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
366 9600, 19200, 38400, 57600, 115200)
368 def open(self):
369 """\
370 Open port with current settings. This may throw a SerialException
371 if the port cannot be opened.
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))
389 self._socket.settimeout(5) # XXX good value?
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),
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),
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),
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
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()
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)
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()
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")
467 # if self._timeout != 0 and self._interCharTimeout is not None:
468 # XXX
470 if self._writeTimeout is not None:
471 raise NotImplementedError('writeTimeout is currently not supported')
472 # XXX
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]))
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,))
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)
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)
523 def makeDeviceName(self, port):
524 raise SerialException("there is no sensible way to turn numbers into URLs")
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)
561 # - - - - - - - - - - - - - - - - - - - - - - - -
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()
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.
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)
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.
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)
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)
610 def flushOutput(self):
611 """\
612 Clear output buffer, aborting the current output and
613 discarding all that is in the buffer.
615 if not self._isOpen: raise portNotOpenError
616 self.rfc2217SendPurge(PURGE_TRANSMIT_BUFFER)
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)
626 def setBreak(self, level=True):
627 """\
628 Set break: Controls TXD. When active, to transmitting is
629 possible.
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)
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)
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)
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)
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)
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)
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)
679 # - - - platform specific - - -
680 # None so far
682 # - - - RFC2217 specific - - -
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")
748 # - incoming telnet commands and options
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,))
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,))
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,))
806 # - outgoing telnet commands and options
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()
816 def telnetSendOption(self, action, option):
817 """Send DO, DONT, WILL, WONT."""
818 self._internal_raw_write(to_bytes([IAC, action, option]))
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]))
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
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
841 def rfc2217FlowServerReady(self):
842 """\
843 check if server is ready to receive data. block for some time when
844 not.
846 #~ if self._remote_suspend_flow:
847 #~ wait---
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.)
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")
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
899 #############################################################################
900 # The following is code that helps implementing an RFC 2217 server.
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.
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
915 # filter state machine
916 self.mode = M_NORMAL
917 self.suboption = None
918 self.telnet_command = None
920 # states for modem/line control events
921 self.modemstate_mask = 255
922 self.last_modemstate = None
923 self.linstate_mask = 0
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),
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
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.
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)
962 # - outgoing telnet commands and options
964 def telnetSendOption(self, action, option):
965 """Send DO, DONT, WILL, WONT."""
966 self.connection.write(to_bytes([IAC, action, option]))
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]))
973 # - check modem lines, needs to be called periodically from user to
974 # establish polling
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)
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])
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
1008 # - outgoing data escaping
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.
1016 socket.sendall(escape(data))
1018 for byte in data:
1019 if byte == IAC:
1020 yield IAC
1021 yield IAC
1022 else:
1023 yield byte
1025 # - incoming data filter
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.
1032 The idea is that the reader thread pushes data from the socket through
1033 this filter:
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)
1040 (socket error handling code left as exercise for the reader)
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
1084 # - incoming telnet commands and options
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,))
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,))
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])
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])
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
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,))
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)
1314 #~ s.baudrate = 1898
1316 sys.stdout.write("write...\n")
1317 s.write("hello\n")
1318 s.flush()
1319 sys.stdout.write("read: %s\n" % s.read(5))
1321 #~ s.baudrate = 19200
1322 #~ s.databits = 7
1323 s.close()