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