You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1352 lines
58KB

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