OLD | NEW |
(Empty) | |
| 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 /v
ar/lock/sredir"; done |
| 30 #============================================================================= |
| 31 # telnetcpcd (untested) |
| 32 # - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz |
| 33 # - To get the signature [COM_PORT_OPTION] w/o data has to be sent. |
| 34 #============================================================================= |
| 35 # ser2net |
| 36 # - does not negotiate BINARY or COM_PORT_OPTION for his side but at least |
| 37 # acknowledges that the client activates these options |
| 38 # - The configuration may be that the server prints a banner. As this client |
| 39 # implementation does a flushInput on connect, this banner is hidden from |
| 40 # the user application. |
| 41 # - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one |
| 42 # second. |
| 43 # - To get the signature [COM_PORT_OPTION 0] has to be sent. |
| 44 # - run a server: run ser2net daemon, in /etc/ser2net.conf: |
| 45 # 2000:telnet:0:/dev/ttyS0:9600 remctl banner |
| 46 ############################################################################## |
| 47 |
| 48 # How to identify ports? pySerial might want to support other protocols in the |
| 49 # future, so lets use an URL scheme. |
| 50 # for RFC2217 compliant servers we will use this: |
| 51 # rfc2217://<host>:<port>[/option[/option...]] |
| 52 # |
| 53 # options: |
| 54 # - "debug" print diagnostic messages |
| 55 # - "ign_set_control": do not look at the answers to SET_CONTROL |
| 56 # - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. |
| 57 # Without this option it expects that the server sends notifications |
| 58 # automatically on change (which most servers do and is according to the |
| 59 # RFC). |
| 60 # the order of the options is not relevant |
| 61 |
| 62 from serial.serialutil import * |
| 63 import time |
| 64 import struct |
| 65 import socket |
| 66 import threading |
| 67 import Queue |
| 68 import logging |
| 69 |
| 70 # port string is expected to be something like this: |
| 71 # rfc2217://host:port |
| 72 # host may be an IP or including domain, whatever. |
| 73 # port is 0...65535 |
| 74 |
| 75 # map log level names to constants. used in fromURL() |
| 76 LOGGER_LEVELS = { |
| 77 'debug': logging.DEBUG, |
| 78 'info': logging.INFO, |
| 79 'warning': logging.WARNING, |
| 80 'error': logging.ERROR, |
| 81 } |
| 82 |
| 83 |
| 84 # telnet protocol characters |
| 85 IAC = to_bytes([255]) # Interpret As Command |
| 86 DONT = to_bytes([254]) |
| 87 DO = to_bytes([253]) |
| 88 WONT = to_bytes([252]) |
| 89 WILL = to_bytes([251]) |
| 90 IAC_DOUBLED = to_bytes([IAC, IAC]) |
| 91 |
| 92 SE = to_bytes([240]) # Subnegotiation End |
| 93 NOP = to_bytes([241]) # No Operation |
| 94 DM = to_bytes([242]) # Data Mark |
| 95 BRK = to_bytes([243]) # Break |
| 96 IP = to_bytes([244]) # Interrupt process |
| 97 AO = to_bytes([245]) # Abort output |
| 98 AYT = to_bytes([246]) # Are You There |
| 99 EC = to_bytes([247]) # Erase Character |
| 100 EL = to_bytes([248]) # Erase Line |
| 101 GA = to_bytes([249]) # Go Ahead |
| 102 SB = to_bytes([250]) # Subnegotiation Begin |
| 103 |
| 104 # selected telnet options |
| 105 BINARY = to_bytes([0]) # 8-bit data path |
| 106 ECHO = to_bytes([1]) # echo |
| 107 SGA = to_bytes([3]) # suppress go ahead |
| 108 |
| 109 # RFC2217 |
| 110 COM_PORT_OPTION = to_bytes([44]) |
| 111 |
| 112 # Client to Access Server |
| 113 SET_BAUDRATE = to_bytes([1]) |
| 114 SET_DATASIZE = to_bytes([2]) |
| 115 SET_PARITY = to_bytes([3]) |
| 116 SET_STOPSIZE = to_bytes([4]) |
| 117 SET_CONTROL = to_bytes([5]) |
| 118 NOTIFY_LINESTATE = to_bytes([6]) |
| 119 NOTIFY_MODEMSTATE = to_bytes([7]) |
| 120 FLOWCONTROL_SUSPEND = to_bytes([8]) |
| 121 FLOWCONTROL_RESUME = to_bytes([9]) |
| 122 SET_LINESTATE_MASK = to_bytes([10]) |
| 123 SET_MODEMSTATE_MASK = to_bytes([11]) |
| 124 PURGE_DATA = to_bytes([12]) |
| 125 |
| 126 SERVER_SET_BAUDRATE = to_bytes([101]) |
| 127 SERVER_SET_DATASIZE = to_bytes([102]) |
| 128 SERVER_SET_PARITY = to_bytes([103]) |
| 129 SERVER_SET_STOPSIZE = to_bytes([104]) |
| 130 SERVER_SET_CONTROL = to_bytes([105]) |
| 131 SERVER_NOTIFY_LINESTATE = to_bytes([106]) |
| 132 SERVER_NOTIFY_MODEMSTATE = to_bytes([107]) |
| 133 SERVER_FLOWCONTROL_SUSPEND = to_bytes([108]) |
| 134 SERVER_FLOWCONTROL_RESUME = to_bytes([109]) |
| 135 SERVER_SET_LINESTATE_MASK = to_bytes([110]) |
| 136 SERVER_SET_MODEMSTATE_MASK = to_bytes([111]) |
| 137 SERVER_PURGE_DATA = to_bytes([112]) |
| 138 |
| 139 RFC2217_ANSWER_MAP = { |
| 140 SET_BAUDRATE: SERVER_SET_BAUDRATE, |
| 141 SET_DATASIZE: SERVER_SET_DATASIZE, |
| 142 SET_PARITY: SERVER_SET_PARITY, |
| 143 SET_STOPSIZE: SERVER_SET_STOPSIZE, |
| 144 SET_CONTROL: SERVER_SET_CONTROL, |
| 145 NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, |
| 146 NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, |
| 147 FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, |
| 148 FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, |
| 149 SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, |
| 150 SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, |
| 151 PURGE_DATA: SERVER_PURGE_DATA, |
| 152 } |
| 153 |
| 154 SET_CONTROL_REQ_FLOW_SETTING = to_bytes([0]) # Request Com Port Flow Cont
rol Setting (outbound/both) |
| 155 SET_CONTROL_USE_NO_FLOW_CONTROL = to_bytes([1]) # Use No Flow Control (outbo
und/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 Cont
rol Setting (inbound) |
| 168 SET_CONTROL_USE_NO_FLOW_CONTROL_IN = to_bytes([14]) # Use No Flow Control (inbou
nd) |
| 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 (outb
ound/both) |
| 172 SET_CONTROL_USE_DTR_FLOW_CONTROL = to_bytes([18]) # Use DTR Flow Control (inbo
und) |
| 173 SET_CONTROL_USE_DSR_FLOW_CONTROL = to_bytes([19]) # Use DSR Flow Control (outb
ound/both) |
| 174 |
| 175 LINESTATE_MASK_TIMEOUT = 128 # Time-out Error |
| 176 LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty |
| 177 LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty |
| 178 LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error |
| 179 LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error |
| 180 LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error |
| 181 LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error |
| 182 LINESTATE_MASK_DATA_READY = 1 # Data Ready |
| 183 |
| 184 MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also k
nown as Carrier Detect) |
| 185 MODEMSTATE_MASK_RI = 64 # Ring Indicator |
| 186 MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State |
| 187 MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State |
| 188 MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect |
| 189 MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector |
| 190 MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready |
| 191 MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send |
| 192 |
| 193 PURGE_RECEIVE_BUFFER = to_bytes([1]) # Purge access server receive data b
uffer |
| 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 recei
ve data buffer and the access server transmit data buffer |
| 196 |
| 197 |
| 198 RFC2217_PARITY_MAP = { |
| 199 PARITY_NONE: 1, |
| 200 PARITY_ODD: 2, |
| 201 PARITY_EVEN: 3, |
| 202 PARITY_MARK: 4, |
| 203 PARITY_SPACE: 5, |
| 204 } |
| 205 RFC2217_REVERSE_PARITY_MAP = dict((v,k) for k,v in RFC2217_PARITY_MAP.items()) |
| 206 |
| 207 RFC2217_STOPBIT_MAP = { |
| 208 STOPBITS_ONE: 1, |
| 209 STOPBITS_ONE_POINT_FIVE: 3, |
| 210 STOPBITS_TWO: 2, |
| 211 } |
| 212 RFC2217_REVERSE_STOPBIT_MAP = dict((v,k) for k,v in RFC2217_STOPBIT_MAP.items()) |
| 213 |
| 214 # Telnet filter states |
| 215 M_NORMAL = 0 |
| 216 M_IAC_SEEN = 1 |
| 217 M_NEGOTIATE = 2 |
| 218 |
| 219 # TelnetOption and TelnetSubnegotiation states |
| 220 REQUESTED = 'REQUESTED' |
| 221 ACTIVE = 'ACTIVE' |
| 222 INACTIVE = 'INACTIVE' |
| 223 REALLY_INACTIVE = 'REALLY_INACTIVE' |
| 224 |
| 225 class TelnetOption(object): |
| 226 """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" |
| 227 |
| 228 def __init__(self, connection, name, option, send_yes, send_no, ack_yes, ack
_no, initial_state, activation_callback=None): |
| 229 """\ |
| 230 Initialize option. |
| 231 :param connection: connection used to transmit answers |
| 232 :param name: a readable name for debug outputs |
| 233 :param send_yes: what to send when option is to be enabled. |
| 234 :param send_no: what to send when option is to be disabled. |
| 235 :param ack_yes: what to expect when remote agrees on option. |
| 236 :param ack_no: what to expect when remote disagrees on option. |
| 237 :param initial_state: options initialized with REQUESTED are tried to |
| 238 be enabled on startup. use INACTIVE for all others. |
| 239 """ |
| 240 self.connection = connection |
| 241 self.name = name |
| 242 self.option = option |
| 243 self.send_yes = send_yes |
| 244 self.send_no = send_no |
| 245 self.ack_yes = ack_yes |
| 246 self.ack_no = ack_no |
| 247 self.state = initial_state |
| 248 self.active = False |
| 249 self.activation_callback = activation_callback |
| 250 |
| 251 def __repr__(self): |
| 252 """String for debug outputs""" |
| 253 return "%s:%s(%s)" % (self.name, self.active, self.state) |
| 254 |
| 255 def process_incoming(self, command): |
| 256 """A DO/DONT/WILL/WONT was received for this option, update state and |
| 257 answer when needed.""" |
| 258 if command == self.ack_yes: |
| 259 if self.state is REQUESTED: |
| 260 self.state = ACTIVE |
| 261 self.active = True |
| 262 if self.activation_callback is not None: |
| 263 self.activation_callback() |
| 264 elif self.state is ACTIVE: |
| 265 pass |
| 266 elif self.state is INACTIVE: |
| 267 self.state = ACTIVE |
| 268 self.connection.telnetSendOption(self.send_yes, self.option) |
| 269 self.active = True |
| 270 if self.activation_callback is not None: |
| 271 self.activation_callback() |
| 272 elif self.state is REALLY_INACTIVE: |
| 273 self.connection.telnetSendOption(self.send_no, self.option) |
| 274 else: |
| 275 raise ValueError('option in illegal state %r' % self) |
| 276 elif command == self.ack_no: |
| 277 if self.state is REQUESTED: |
| 278 self.state = INACTIVE |
| 279 self.active = False |
| 280 elif self.state is ACTIVE: |
| 281 self.state = INACTIVE |
| 282 self.connection.telnetSendOption(self.send_no, self.option) |
| 283 self.active = False |
| 284 elif self.state is INACTIVE: |
| 285 pass |
| 286 elif self.state is REALLY_INACTIVE: |
| 287 pass |
| 288 else: |
| 289 raise ValueError('option in illegal state %r' % self) |
| 290 |
| 291 |
| 292 class TelnetSubnegotiation(object): |
| 293 """\ |
| 294 A object to handle subnegotiation of options. In this case actually |
| 295 sub-sub options for RFC 2217. It is used to track com port options. |
| 296 """ |
| 297 |
| 298 def __init__(self, connection, name, option, ack_option=None): |
| 299 if ack_option is None: ack_option = option |
| 300 self.connection = connection |
| 301 self.name = name |
| 302 self.option = option |
| 303 self.value = None |
| 304 self.ack_option = ack_option |
| 305 self.state = INACTIVE |
| 306 |
| 307 def __repr__(self): |
| 308 """String for debug outputs.""" |
| 309 return "%s:%s" % (self.name, self.state) |
| 310 |
| 311 def set(self, value): |
| 312 """\ |
| 313 request a change of the value. a request is sent to the server. if |
| 314 the client needs to know if the change is performed he has to check the |
| 315 state of this object. |
| 316 """ |
| 317 self.value = value |
| 318 self.state = REQUESTED |
| 319 self.connection.rfc2217SendSubnegotiation(self.option, self.value) |
| 320 if self.connection.logger: |
| 321 self.connection.logger.debug("SB Requesting %s -> %r" % (self.name,
self.value)) |
| 322 |
| 323 def isReady(self): |
| 324 """\ |
| 325 check if answer from server has been received. when server rejects |
| 326 the change, raise a ValueError. |
| 327 """ |
| 328 if self.state == REALLY_INACTIVE: |
| 329 raise ValueError("remote rejected value for option %r" % (self.name)
) |
| 330 return self.state == ACTIVE |
| 331 # add property to have a similar interface as TelnetOption |
| 332 active = property(isReady) |
| 333 |
| 334 def wait(self, timeout=3): |
| 335 """\ |
| 336 wait until the subnegotiation has been acknowledged or timeout. It |
| 337 can also throw a value error when the answer from the server does not |
| 338 match the value sent. |
| 339 """ |
| 340 timeout_time = time.time() + timeout |
| 341 while time.time() < timeout_time: |
| 342 time.sleep(0.05) # prevent 100% CPU load |
| 343 if self.isReady(): |
| 344 break |
| 345 else: |
| 346 raise SerialException("timeout while waiting for option %r" % (self.
name)) |
| 347 |
| 348 def checkAnswer(self, suboption): |
| 349 """\ |
| 350 check an incoming subnegotiation block. the parameter already has |
| 351 cut off the header like sub option number and com port option value. |
| 352 """ |
| 353 if self.value == suboption[:len(self.value)]: |
| 354 self.state = ACTIVE |
| 355 else: |
| 356 # error propagation done in isReady |
| 357 self.state = REALLY_INACTIVE |
| 358 if self.connection.logger: |
| 359 self.connection.logger.debug("SB Answer %s -> %r -> %s" % (self.name
, suboption, self.state)) |
| 360 |
| 361 |
| 362 class RFC2217Serial(SerialBase): |
| 363 """Serial port implementation for RFC 2217 remote serial ports.""" |
| 364 |
| 365 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, |
| 366 9600, 19200, 38400, 57600, 115200) |
| 367 |
| 368 def open(self): |
| 369 """\ |
| 370 Open port with current settings. This may throw a SerialException |
| 371 if the port cannot be opened. |
| 372 """ |
| 373 self.logger = None |
| 374 self._ignore_set_control_answer = False |
| 375 self._poll_modem_state = False |
| 376 self._network_timeout = 3 |
| 377 if self._port is None: |
| 378 raise SerialException("Port must be configured before it can be used
.") |
| 379 if self._isOpen: |
| 380 raise SerialException("Port is already open.") |
| 381 try: |
| 382 self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| 383 self._socket.connect(self.fromURL(self.portstr)) |
| 384 self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) |
| 385 except Exception, msg: |
| 386 self._socket = None |
| 387 raise SerialException("Could not open port %s: %s" % (self.portstr,
msg)) |
| 388 |
| 389 self._socket.settimeout(5) # XXX good value? |
| 390 |
| 391 # use a thread save queue as buffer. it also simplifies implementing |
| 392 # the read timeout |
| 393 self._read_buffer = Queue.Queue() |
| 394 # to ensure that user writes does not interfere with internal |
| 395 # telnet/rfc2217 options establish a lock |
| 396 self._write_lock = threading.Lock() |
| 397 # name the following separately so that, below, a check can be easily do
ne |
| 398 mandadory_options = [ |
| 399 TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTI
VE), |
| 400 TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DO
NT, 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, INAC
TIVE), |
| 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, SER
VER_SET_BAUDRATE), |
| 414 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SER
VER_SET_DATASIZE), |
| 415 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SER
VER_SET_PARITY), |
| 416 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SER
VER_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, SER
VER_PURGE_DATA), |
| 422 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SER
VER_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._p
ort,)) |
| 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 BI
NARY 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._baud
rate)) |
| 479 self._rfc2217_port_settings['datasize'].set(struct.pack('!B', self._byte
size)) |
| 480 self._rfc2217_port_settings['parity'].set(struct.pack('!B', RFC2217_PARI
TY_MAP[self._parity])) |
| 481 self._rfc2217_port_settings['stopsize'].set(struct.pack('!B', RFC2217_ST
OPBIT_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 (RFC2
217): %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 URL
s") |
| 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 he
re? |
| 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 unp
acking |
| 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...65
535") |
| 557 except ValueError, e: |
| 558 raise SerialException('expected a string in the form "[rfc2217://]<h
ost>:<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(lev
el)]) |
| 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 receive
d, 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(subopti
on[1:]) |
| 797 item.checkAnswer(bytes(suboption[2:])) |
| 798 break |
| 799 else: |
| 800 if self.logger: |
| 801 self.logger.warning("ignoring COM_PORT_OPTION: %r" % (su
boption,)) |
| 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 stuf
f.""" |
| 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] + l
ist(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 se
rver |
| 840 |
| 841 def rfc2217FlowServerReady(self): |
| 842 """\ |
| 843 check if server is ready to receive data. block for some time when |
| 844 not. |
| 845 """ |
| 846 #~ if self._remote_suspend_flow: |
| 847 #~ wait--- |
| 848 |
| 849 def getModemState(self): |
| 850 """\ |
| 851 get last modem state (cached value. if value is "old", request a new |
| 852 one. this cache helps that we don't issue to many requests when e.g. all |
| 853 status lines, one after the other is queried by te user (getCTS, getDSR |
| 854 etc.) |
| 855 """ |
| 856 # active modem state polling enabled? is the value fresh enough? |
| 857 if self._poll_modem_state and self._modemstate_expires < time.time(): |
| 858 if self.logger: |
| 859 self.logger.debug('polling modem state') |
| 860 # when it is older, request an update |
| 861 self.rfc2217SendSubnegotiation(NOTIFY_MODEMSTATE) |
| 862 timeout_time = time.time() + self._network_timeout |
| 863 while time.time() < timeout_time: |
| 864 time.sleep(0.05) # prevent 100% CPU load |
| 865 # when expiration time is updated, it means that there is a new |
| 866 # value |
| 867 if self._modemstate_expires > time.time(): |
| 868 if self.logger: |
| 869 self.logger.warning('poll for modem state failed') |
| 870 break |
| 871 # even when there is a timeout, do not generate an error just |
| 872 # return the last known value. this way we can support buggy |
| 873 # servers that do not respond to polls, but send automatic |
| 874 # updates. |
| 875 if self._modemstate is not None: |
| 876 if self.logger: |
| 877 self.logger.debug('using cached modem state') |
| 878 return self._modemstate |
| 879 else: |
| 880 # never received a notification from the server |
| 881 raise SerialException("remote sends no NOTIFY_MODEMSTATE") |
| 882 |
| 883 |
| 884 # assemble Serial class with the platform specific implementation and the base |
| 885 # for file-like behavior. for Python 2.6 and newer, that provide the new I/O |
| 886 # library, derive from io.RawIOBase |
| 887 try: |
| 888 import io |
| 889 except ImportError: |
| 890 # classic version with our own file-like emulation |
| 891 class Serial(RFC2217Serial, FileLike): |
| 892 pass |
| 893 else: |
| 894 # io library present |
| 895 class Serial(RFC2217Serial, io.RawIOBase): |
| 896 pass |
| 897 |
| 898 |
| 899 ############################################################################# |
| 900 # The following is code that helps implementing an RFC 2217 server. |
| 901 |
| 902 class PortManager(object): |
| 903 """\ |
| 904 This class manages the state of Telnet and RFC 2217. It needs a serial |
| 905 instance and a connection to work with. Connection is expected to implement |
| 906 a (thread safe) write function, that writes the string to the network. |
| 907 """ |
| 908 |
| 909 def __init__(self, serial_port, connection, logger=None): |
| 910 self.serial = serial_port |
| 911 self.connection = connection |
| 912 self.logger = logger |
| 913 self._client_is_rfc2217 = False |
| 914 |
| 915 # filter state machine |
| 916 self.mode = M_NORMAL |
| 917 self.suboption = None |
| 918 self.telnet_command = None |
| 919 |
| 920 # states for modem/line control events |
| 921 self.modemstate_mask = 255 |
| 922 self.last_modemstate = None |
| 923 self.linstate_mask = 0 |
| 924 |
| 925 # all supported telnet options |
| 926 self._telnet_options = [ |
| 927 TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), |
| 928 TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), |
| 929 TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), |
| 930 TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTI
VE), |
| 931 TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQU
ESTED), |
| 932 TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DO
NT, 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[pari
ty] |
| 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[stopb
its] |
| 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_C
ONTROL_USE_SW_FLOW_CONTROL) |
| 1183 elif self.serial.rtscts: |
| 1184 self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_C
ONTROL_USE_HW_FLOW_CONTROL) |
| 1185 else: |
| 1186 self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_C
ONTROL_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_CONTR
OL_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_CONTR
OL_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_CONTR
OL_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 impleme
nted") |
| 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_CONTR
OL_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_CONTR
OL_BREAK_OFF) |
| 1217 elif suboption[2:3] == SET_CONTROL_REQ_DTR: |
| 1218 if self.logger: |
| 1219 self.logger.warning("requested DTR state - not implement
ed") |
| 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_CONTR
OL_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_CONTR
OL_DTR_OFF) |
| 1231 elif suboption[2:3] == SET_CONTROL_REQ_RTS: |
| 1232 if self.logger: |
| 1233 self.logger.warning("requested RTS state - not implement
ed") |
| 1234 pass # XXX needs cached value |
| 1235 #~ self.rfc2217SendSubnegotiation(SERVER_SET_CONTROL, SET_CO
NTROL_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_CONTR
OL_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_CONTR
OL_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 numb
er |
| 1278 if self.logger: |
| 1279 self.logger.info("modem state mask: 0x%02x" % (self.modemsta
te_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_RECE
IVE_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_TRAN
SMIT_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(subo
ption[2:])) |
| 1300 else: |
| 1301 if self.logger: |
| 1302 self.logger.error("undefined COM_PORT_OPTION: %r" % list(sub
option[1:])) |
| 1303 else: |
| 1304 if self.logger: |
| 1305 self.logger.warning("unknown subnegotiation: %r" % (suboption,)) |
| 1306 |
| 1307 |
| 1308 # simple client test |
| 1309 if __name__ == '__main__': |
| 1310 import sys |
| 1311 s = Serial('rfc2217://localhost:7000', 115200) |
| 1312 sys.stdout.write('%s\n' % s) |
| 1313 |
| 1314 #~ s.baudrate = 1898 |
| 1315 |
| 1316 sys.stdout.write("write...\n") |
| 1317 s.write("hello\n") |
| 1318 s.flush() |
| 1319 sys.stdout.write("read: %s\n" % s.read(5)) |
| 1320 |
| 1321 #~ s.baudrate = 19200 |
| 1322 #~ s.databits = 7 |
| 1323 s.close() |
OLD | NEW |