rfc2217.py 58 KB

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