rfc2217.py 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346
  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. self._thread = None
  344. self._socket = None
  345. self._linestate = 0
  346. self._modemstate = None
  347. self._modemstate_timeout = Timeout(-1)
  348. self._remote_suspend_flow = False
  349. self._write_lock = None
  350. self.logger = None
  351. self._ignore_set_control_answer = False
  352. self._poll_modem_state = False
  353. self._network_timeout = 3
  354. self._telnet_options = None
  355. self._rfc2217_port_settings = None
  356. self._rfc2217_options = None
  357. self._read_buffer = None
  358. super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open
  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. timeout = Timeout(self._timeout)
  557. while len(data) < size:
  558. if self._thread is None:
  559. raise SerialException('connection failed (reader thread died)')
  560. data += self._read_buffer.get(True, timeout.time_left())
  561. if timeout.expired():
  562. break
  563. except Queue.Empty: # -> timeout
  564. pass
  565. return bytes(data)
  566. def write(self, data):
  567. """\
  568. Output the given byte string over the serial port. Can block if the
  569. connection is blocked. May raise SerialException if the connection is
  570. closed.
  571. """
  572. if not self.is_open:
  573. raise portNotOpenError
  574. # XXX use protocol_socket's write
  575. with self._write_lock:
  576. try:
  577. self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED))
  578. except socket.error as e:
  579. raise SerialException("connection failed (socket error): {}".format(e))
  580. return len(data)
  581. def reset_input_buffer(self):
  582. """Clear input buffer, discarding all that is in the buffer."""
  583. if not self.is_open:
  584. raise portNotOpenError
  585. self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER)
  586. # empty read buffer
  587. while self._read_buffer.qsize():
  588. self._read_buffer.get(False)
  589. def reset_output_buffer(self):
  590. """\
  591. Clear output buffer, aborting the current output and
  592. discarding all that is in the buffer.
  593. """
  594. if not self.is_open:
  595. raise portNotOpenError
  596. self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER)
  597. def _update_break_state(self):
  598. """\
  599. Set break: Controls TXD. When active, to transmitting is
  600. possible.
  601. """
  602. if not self.is_open:
  603. raise portNotOpenError
  604. if self.logger:
  605. self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive'))
  606. if self._break_state:
  607. self.rfc2217_set_control(SET_CONTROL_BREAK_ON)
  608. else:
  609. self.rfc2217_set_control(SET_CONTROL_BREAK_OFF)
  610. def _update_rts_state(self):
  611. """Set terminal status line: Request To Send."""
  612. if not self.is_open:
  613. raise portNotOpenError
  614. if self.logger:
  615. self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive'))
  616. if self._rts_state:
  617. self.rfc2217_set_control(SET_CONTROL_RTS_ON)
  618. else:
  619. self.rfc2217_set_control(SET_CONTROL_RTS_OFF)
  620. def _update_dtr_state(self):
  621. """Set terminal status line: Data Terminal Ready."""
  622. if not self.is_open:
  623. raise portNotOpenError
  624. if self.logger:
  625. self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive'))
  626. if self._dtr_state:
  627. self.rfc2217_set_control(SET_CONTROL_DTR_ON)
  628. else:
  629. self.rfc2217_set_control(SET_CONTROL_DTR_OFF)
  630. @property
  631. def cts(self):
  632. """Read terminal status line: Clear To Send."""
  633. if not self.is_open:
  634. raise portNotOpenError
  635. return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS)
  636. @property
  637. def dsr(self):
  638. """Read terminal status line: Data Set Ready."""
  639. if not self.is_open:
  640. raise portNotOpenError
  641. return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR)
  642. @property
  643. def ri(self):
  644. """Read terminal status line: Ring Indicator."""
  645. if not self.is_open:
  646. raise portNotOpenError
  647. return bool(self.get_modem_state() & MODEMSTATE_MASK_RI)
  648. @property
  649. def cd(self):
  650. """Read terminal status line: Carrier Detect."""
  651. if not self.is_open:
  652. raise portNotOpenError
  653. return bool(self.get_modem_state() & MODEMSTATE_MASK_CD)
  654. # - - - platform specific - - -
  655. # None so far
  656. # - - - RFC2217 specific - - -
  657. def _telnet_read_loop(self):
  658. """Read loop for the socket."""
  659. mode = M_NORMAL
  660. suboption = None
  661. try:
  662. while self.is_open:
  663. try:
  664. data = self._socket.recv(1024)
  665. except socket.timeout:
  666. # just need to get out of recv form time to time to check if
  667. # still alive
  668. continue
  669. except socket.error as e:
  670. # connection fails -> terminate loop
  671. if self.logger:
  672. self.logger.debug("socket error in reader thread: {}".format(e))
  673. break
  674. if not data:
  675. break # lost connection
  676. for byte in iterbytes(data):
  677. if mode == M_NORMAL:
  678. # interpret as command or as data
  679. if byte == IAC:
  680. mode = M_IAC_SEEN
  681. else:
  682. # store data in read buffer or sub option buffer
  683. # depending on state
  684. if suboption is not None:
  685. suboption += byte
  686. else:
  687. self._read_buffer.put(byte)
  688. elif mode == M_IAC_SEEN:
  689. if byte == IAC:
  690. # interpret as command doubled -> insert character
  691. # itself
  692. if suboption is not None:
  693. suboption += IAC
  694. else:
  695. self._read_buffer.put(IAC)
  696. mode = M_NORMAL
  697. elif byte == SB:
  698. # sub option start
  699. suboption = bytearray()
  700. mode = M_NORMAL
  701. elif byte == SE:
  702. # sub option end -> process it now
  703. self._telnet_process_subnegotiation(bytes(suboption))
  704. suboption = None
  705. mode = M_NORMAL
  706. elif byte in (DO, DONT, WILL, WONT):
  707. # negotiation
  708. telnet_command = byte
  709. mode = M_NEGOTIATE
  710. else:
  711. # other telnet commands
  712. self._telnet_process_command(byte)
  713. mode = M_NORMAL
  714. elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
  715. self._telnet_negotiate_option(telnet_command, byte)
  716. mode = M_NORMAL
  717. finally:
  718. self._thread = None
  719. if self.logger:
  720. self.logger.debug("read thread terminated")
  721. # - incoming telnet commands and options
  722. def _telnet_process_command(self, command):
  723. """Process commands other than DO, DONT, WILL, WONT."""
  724. # Currently none. RFC2217 only uses negotiation and subnegotiation.
  725. if self.logger:
  726. self.logger.warning("ignoring Telnet command: {!r}".format(command))
  727. def _telnet_negotiate_option(self, command, option):
  728. """Process incoming DO, DONT, WILL, WONT."""
  729. # check our registered telnet options and forward command to them
  730. # they know themselves if they have to answer or not
  731. known = False
  732. for item in self._telnet_options:
  733. # can have more than one match! as some options are duplicated for
  734. # 'us' and 'them'
  735. if item.option == option:
  736. item.process_incoming(command)
  737. known = True
  738. if not known:
  739. # handle unknown options
  740. # only answer to positive requests and deny them
  741. if command == WILL or command == DO:
  742. self.telnet_send_option((DONT if command == WILL else WONT), option)
  743. if self.logger:
  744. self.logger.warning("rejected Telnet option: {!r}".format(option))
  745. def _telnet_process_subnegotiation(self, suboption):
  746. """Process subnegotiation, the data between IAC SB and IAC SE."""
  747. if suboption[0:1] == COM_PORT_OPTION:
  748. if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3:
  749. self._linestate = ord(suboption[2:3]) # ensure it is a number
  750. if self.logger:
  751. self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate))
  752. elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3:
  753. self._modemstate = ord(suboption[2:3]) # ensure it is a number
  754. if self.logger:
  755. self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate))
  756. # update time when we think that a poll would make sense
  757. self._modemstate_timeout.restart(0.3)
  758. elif suboption[1:2] == FLOWCONTROL_SUSPEND:
  759. self._remote_suspend_flow = True
  760. elif suboption[1:2] == FLOWCONTROL_RESUME:
  761. self._remote_suspend_flow = False
  762. else:
  763. for item in self._rfc2217_options.values():
  764. if item.ack_option == suboption[1:2]:
  765. #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
  766. item.check_answer(bytes(suboption[2:]))
  767. break
  768. else:
  769. if self.logger:
  770. self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption))
  771. else:
  772. if self.logger:
  773. self.logger.warning("ignoring subnegotiation: {!r}".format(suboption))
  774. # - outgoing telnet commands and options
  775. def _internal_raw_write(self, data):
  776. """internal socket write with no data escaping. used to send telnet stuff."""
  777. with self._write_lock:
  778. self._socket.sendall(data)
  779. def telnet_send_option(self, action, option):
  780. """Send DO, DONT, WILL, WONT."""
  781. self._internal_raw_write(IAC + action + option)
  782. def rfc2217_send_subnegotiation(self, option, value=b''):
  783. """Subnegotiation of RFC2217 parameters."""
  784. value = value.replace(IAC, IAC_DOUBLED)
  785. self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE)
  786. def rfc2217_send_purge(self, value):
  787. """\
  788. Send purge request to the remote.
  789. (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS)
  790. """
  791. item = self._rfc2217_options['purge']
  792. item.set(value) # transmit desired purge type
  793. item.wait(self._network_timeout) # wait for acknowledge from the server
  794. def rfc2217_set_control(self, value):
  795. """transmit change of control line to remote"""
  796. item = self._rfc2217_options['control']
  797. item.set(value) # transmit desired control type
  798. if self._ignore_set_control_answer:
  799. # answers are ignored when option is set. compatibility mode for
  800. # servers that answer, but not the expected one... (or no answer
  801. # at all) i.e. sredird
  802. time.sleep(0.1) # this helps getting the unit tests passed
  803. else:
  804. item.wait(self._network_timeout) # wait for acknowledge from the server
  805. def rfc2217_flow_server_ready(self):
  806. """\
  807. check if server is ready to receive data. block for some time when
  808. not.
  809. """
  810. #~ if self._remote_suspend_flow:
  811. #~ wait---
  812. def get_modem_state(self):
  813. """\
  814. get last modem state (cached value. If value is "old", request a new
  815. one. This cache helps that we don't issue to many requests when e.g. all
  816. status lines, one after the other is queried by the user (CTS, DSR
  817. etc.)
  818. """
  819. # active modem state polling enabled? is the value fresh enough?
  820. if self._poll_modem_state and self._modemstate_timeout.expired():
  821. if self.logger:
  822. self.logger.debug('polling modem state')
  823. # when it is older, request an update
  824. self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE)
  825. timeout = Timeout(self._network_timeout)
  826. while not timeout.expired():
  827. time.sleep(0.05) # prevent 100% CPU load
  828. # when expiration time is updated, it means that there is a new
  829. # value
  830. if not self._modemstate_timeout.expired():
  831. break
  832. else:
  833. if self.logger:
  834. self.logger.warning('poll for modem state failed')
  835. # even when there is a timeout, do not generate an error just
  836. # return the last known value. this way we can support buggy
  837. # servers that do not respond to polls, but send automatic
  838. # updates.
  839. if self._modemstate is not None:
  840. if self.logger:
  841. self.logger.debug('using cached modem state')
  842. return self._modemstate
  843. else:
  844. # never received a notification from the server
  845. raise SerialException("remote sends no NOTIFY_MODEMSTATE")
  846. #############################################################################
  847. # The following is code that helps implementing an RFC 2217 server.
  848. class PortManager(object):
  849. """\
  850. This class manages the state of Telnet and RFC 2217. It needs a serial
  851. instance and a connection to work with. Connection is expected to implement
  852. a (thread safe) write function, that writes the string to the network.
  853. """
  854. def __init__(self, serial_port, connection, logger=None):
  855. self.serial = serial_port
  856. self.connection = connection
  857. self.logger = logger
  858. self._client_is_rfc2217 = False
  859. # filter state machine
  860. self.mode = M_NORMAL
  861. self.suboption = None
  862. self.telnet_command = None
  863. # states for modem/line control events
  864. self.modemstate_mask = 255
  865. self.last_modemstate = None
  866. self.linstate_mask = 0
  867. # all supported telnet options
  868. self._telnet_options = [
  869. TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
  870. TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
  871. TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
  872. TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
  873. TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED),
  874. TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok),
  875. TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok),
  876. ]
  877. # negotiate Telnet/RFC2217 -> send initial requests
  878. if self.logger:
  879. self.logger.debug("requesting initial Telnet/RFC 2217 options")
  880. for option in self._telnet_options:
  881. if option.state is REQUESTED:
  882. self.telnet_send_option(option.send_yes, option.option)
  883. # issue 1st modem state notification
  884. def _client_ok(self):
  885. """\
  886. callback of telnet option. It gets called when option is activated.
  887. This one here is used to detect when the client agrees on RFC 2217. A
  888. flag is set so that other functions like check_modem_lines know if the
  889. client is OK.
  890. """
  891. # The callback is used for we and they so if one party agrees, we're
  892. # already happy. it seems not all servers do the negotiation correctly
  893. # and i guess there are incorrect clients too.. so be happy if client
  894. # answers one or the other positively.
  895. self._client_is_rfc2217 = True
  896. if self.logger:
  897. self.logger.info("client accepts RFC 2217")
  898. # this is to ensure that the client gets a notification, even if there
  899. # was no change
  900. self.check_modem_lines(force_notification=True)
  901. # - outgoing telnet commands and options
  902. def telnet_send_option(self, action, option):
  903. """Send DO, DONT, WILL, WONT."""
  904. self.connection.write(IAC + action + option)
  905. def rfc2217_send_subnegotiation(self, option, value=b''):
  906. """Subnegotiation of RFC 2217 parameters."""
  907. value = value.replace(IAC, IAC_DOUBLED)
  908. self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE)
  909. # - check modem lines, needs to be called periodically from user to
  910. # establish polling
  911. def check_modem_lines(self, force_notification=False):
  912. """\
  913. read control lines from serial port and compare the last value sent to remote.
  914. send updates on changes.
  915. """
  916. modemstate = (
  917. (self.serial.cts and MODEMSTATE_MASK_CTS) |
  918. (self.serial.dsr and MODEMSTATE_MASK_DSR) |
  919. (self.serial.ri and MODEMSTATE_MASK_RI) |
  920. (self.serial.cd and MODEMSTATE_MASK_CD))
  921. # check what has changed
  922. deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0
  923. if deltas & MODEMSTATE_MASK_CTS:
  924. modemstate |= MODEMSTATE_MASK_CTS_CHANGE
  925. if deltas & MODEMSTATE_MASK_DSR:
  926. modemstate |= MODEMSTATE_MASK_DSR_CHANGE
  927. if deltas & MODEMSTATE_MASK_RI:
  928. modemstate |= MODEMSTATE_MASK_RI_CHANGE
  929. if deltas & MODEMSTATE_MASK_CD:
  930. modemstate |= MODEMSTATE_MASK_CD_CHANGE
  931. # if new state is different and the mask allows this change, send
  932. # notification. suppress notifications when client is not rfc2217
  933. if modemstate != self.last_modemstate or force_notification:
  934. if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification:
  935. self.rfc2217_send_subnegotiation(
  936. SERVER_NOTIFY_MODEMSTATE,
  937. to_bytes([modemstate & self.modemstate_mask]))
  938. if self.logger:
  939. self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate))
  940. # save last state, but forget about deltas.
  941. # otherwise it would also notify about changing deltas which is
  942. # probably not very useful
  943. self.last_modemstate = modemstate & 0xf0
  944. # - outgoing data escaping
  945. def escape(self, data):
  946. """\
  947. This generator function is for the user. All outgoing data has to be
  948. properly escaped, so that no IAC character in the data stream messes up
  949. the Telnet state machine in the server.
  950. socket.sendall(escape(data))
  951. """
  952. for byte in iterbytes(data):
  953. if byte == IAC:
  954. yield IAC
  955. yield IAC
  956. else:
  957. yield byte
  958. # - incoming data filter
  959. def filter(self, data):
  960. """\
  961. Handle a bunch of incoming bytes. This is a generator. It will yield
  962. all characters not of interest for Telnet/RFC 2217.
  963. The idea is that the reader thread pushes data from the socket through
  964. this filter:
  965. for byte in filter(socket.recv(1024)):
  966. # do things like CR/LF conversion/whatever
  967. # and write data to the serial port
  968. serial.write(byte)
  969. (socket error handling code left as exercise for the reader)
  970. """
  971. for byte in iterbytes(data):
  972. if self.mode == M_NORMAL:
  973. # interpret as command or as data
  974. if byte == IAC:
  975. self.mode = M_IAC_SEEN
  976. else:
  977. # store data in sub option buffer or pass it to our
  978. # consumer depending on state
  979. if self.suboption is not None:
  980. self.suboption += byte
  981. else:
  982. yield byte
  983. elif self.mode == M_IAC_SEEN:
  984. if byte == IAC:
  985. # interpret as command doubled -> insert character
  986. # itself
  987. if self.suboption is not None:
  988. self.suboption += byte
  989. else:
  990. yield byte
  991. self.mode = M_NORMAL
  992. elif byte == SB:
  993. # sub option start
  994. self.suboption = bytearray()
  995. self.mode = M_NORMAL
  996. elif byte == SE:
  997. # sub option end -> process it now
  998. self._telnet_process_subnegotiation(bytes(self.suboption))
  999. self.suboption = None
  1000. self.mode = M_NORMAL
  1001. elif byte in (DO, DONT, WILL, WONT):
  1002. # negotiation
  1003. self.telnet_command = byte
  1004. self.mode = M_NEGOTIATE
  1005. else:
  1006. # other telnet commands
  1007. self._telnet_process_command(byte)
  1008. self.mode = M_NORMAL
  1009. elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following
  1010. self._telnet_negotiate_option(self.telnet_command, byte)
  1011. self.mode = M_NORMAL
  1012. # - incoming telnet commands and options
  1013. def _telnet_process_command(self, command):
  1014. """Process commands other than DO, DONT, WILL, WONT."""
  1015. # Currently none. RFC2217 only uses negotiation and subnegotiation.
  1016. if self.logger:
  1017. self.logger.warning("ignoring Telnet command: {!r}".format(command))
  1018. def _telnet_negotiate_option(self, command, option):
  1019. """Process incoming DO, DONT, WILL, WONT."""
  1020. # check our registered telnet options and forward command to them
  1021. # they know themselves if they have to answer or not
  1022. known = False
  1023. for item in self._telnet_options:
  1024. # can have more than one match! as some options are duplicated for
  1025. # 'us' and 'them'
  1026. if item.option == option:
  1027. item.process_incoming(command)
  1028. known = True
  1029. if not known:
  1030. # handle unknown options
  1031. # only answer to positive requests and deny them
  1032. if command == WILL or command == DO:
  1033. self.telnet_send_option((DONT if command == WILL else WONT), option)
  1034. if self.logger:
  1035. self.logger.warning("rejected Telnet option: {!r}".format(option))
  1036. def _telnet_process_subnegotiation(self, suboption):
  1037. """Process subnegotiation, the data between IAC SB and IAC SE."""
  1038. if suboption[0:1] == COM_PORT_OPTION:
  1039. if self.logger:
  1040. self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption))
  1041. if suboption[1:2] == SET_BAUDRATE:
  1042. backup = self.serial.baudrate
  1043. try:
  1044. (baudrate,) = struct.unpack(b"!I", suboption[2:6])
  1045. if baudrate != 0:
  1046. self.serial.baudrate = baudrate
  1047. except ValueError as e:
  1048. if self.logger:
  1049. self.logger.error("failed to set baud rate: {}".format(e))
  1050. self.serial.baudrate = backup
  1051. else:
  1052. if self.logger:
  1053. self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate))
  1054. self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate))
  1055. elif suboption[1:2] == SET_DATASIZE:
  1056. backup = self.serial.bytesize
  1057. try:
  1058. (datasize,) = struct.unpack(b"!B", suboption[2:3])
  1059. if datasize != 0:
  1060. self.serial.bytesize = datasize
  1061. except ValueError as e:
  1062. if self.logger:
  1063. self.logger.error("failed to set data size: {}".format(e))
  1064. self.serial.bytesize = backup
  1065. else:
  1066. if self.logger:
  1067. self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize))
  1068. self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize))
  1069. elif suboption[1:2] == SET_PARITY:
  1070. backup = self.serial.parity
  1071. try:
  1072. parity = struct.unpack(b"!B", suboption[2:3])[0]
  1073. if parity != 0:
  1074. self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity]
  1075. except ValueError as e:
  1076. if self.logger:
  1077. self.logger.error("failed to set parity: {}".format(e))
  1078. self.serial.parity = backup
  1079. else:
  1080. if self.logger:
  1081. self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity))
  1082. self.rfc2217_send_subnegotiation(
  1083. SERVER_SET_PARITY,
  1084. struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity]))
  1085. elif suboption[1:2] == SET_STOPSIZE:
  1086. backup = self.serial.stopbits
  1087. try:
  1088. stopbits = struct.unpack(b"!B", suboption[2:3])[0]
  1089. if stopbits != 0:
  1090. self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits]
  1091. except ValueError as e:
  1092. if self.logger:
  1093. self.logger.error("failed to set stop bits: {}".format(e))
  1094. self.serial.stopbits = backup
  1095. else:
  1096. if self.logger:
  1097. self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits))
  1098. self.rfc2217_send_subnegotiation(
  1099. SERVER_SET_STOPSIZE,
  1100. struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits]))
  1101. elif suboption[1:2] == SET_CONTROL:
  1102. if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING:
  1103. if self.serial.xonxoff:
  1104. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
  1105. elif self.serial.rtscts:
  1106. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
  1107. else:
  1108. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
  1109. elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL:
  1110. self.serial.xonxoff = False
  1111. self.serial.rtscts = False
  1112. if self.logger:
  1113. self.logger.info("changed flow control to None")
  1114. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
  1115. elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL:
  1116. self.serial.xonxoff = True
  1117. if self.logger:
  1118. self.logger.info("changed flow control to XON/XOFF")
  1119. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
  1120. elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL:
  1121. self.serial.rtscts = True
  1122. if self.logger:
  1123. self.logger.info("changed flow control to RTS/CTS")
  1124. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
  1125. elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE:
  1126. if self.logger:
  1127. self.logger.warning("requested break state - not implemented")
  1128. pass # XXX needs cached value
  1129. elif suboption[2:3] == SET_CONTROL_BREAK_ON:
  1130. self.serial.break_condition = True
  1131. if self.logger:
  1132. self.logger.info("changed BREAK to active")
  1133. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON)
  1134. elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
  1135. self.serial.break_condition = False
  1136. if self.logger:
  1137. self.logger.info("changed BREAK to inactive")
  1138. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF)
  1139. elif suboption[2:3] == SET_CONTROL_REQ_DTR:
  1140. if self.logger:
  1141. self.logger.warning("requested DTR state - not implemented")
  1142. pass # XXX needs cached value
  1143. elif suboption[2:3] == SET_CONTROL_DTR_ON:
  1144. self.serial.dtr = True
  1145. if self.logger:
  1146. self.logger.info("changed DTR to active")
  1147. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON)
  1148. elif suboption[2:3] == SET_CONTROL_DTR_OFF:
  1149. self.serial.dtr = False
  1150. if self.logger:
  1151. self.logger.info("changed DTR to inactive")
  1152. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF)
  1153. elif suboption[2:3] == SET_CONTROL_REQ_RTS:
  1154. if self.logger:
  1155. self.logger.warning("requested RTS state - not implemented")
  1156. pass # XXX needs cached value
  1157. #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
  1158. elif suboption[2:3] == SET_CONTROL_RTS_ON:
  1159. self.serial.rts = True
  1160. if self.logger:
  1161. self.logger.info("changed RTS to active")
  1162. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
  1163. elif suboption[2:3] == SET_CONTROL_RTS_OFF:
  1164. self.serial.rts = False
  1165. if self.logger:
  1166. self.logger.info("changed RTS to inactive")
  1167. self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF)
  1168. #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
  1169. #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
  1170. #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
  1171. #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
  1172. #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
  1173. #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
  1174. #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
  1175. elif suboption[1:2] == NOTIFY_LINESTATE:
  1176. # client polls for current state
  1177. self.rfc2217_send_subnegotiation(
  1178. SERVER_NOTIFY_LINESTATE,
  1179. to_bytes([0])) # sorry, nothing like that implemented
  1180. elif suboption[1:2] == NOTIFY_MODEMSTATE:
  1181. if self.logger:
  1182. self.logger.info("request for modem state")
  1183. # client polls for current state
  1184. self.check_modem_lines(force_notification=True)
  1185. elif suboption[1:2] == FLOWCONTROL_SUSPEND:
  1186. if self.logger:
  1187. self.logger.info("suspend")
  1188. self._remote_suspend_flow = True
  1189. elif suboption[1:2] == FLOWCONTROL_RESUME:
  1190. if self.logger:
  1191. self.logger.info("resume")
  1192. self._remote_suspend_flow = False
  1193. elif suboption[1:2] == SET_LINESTATE_MASK:
  1194. self.linstate_mask = ord(suboption[2:3]) # ensure it is a number
  1195. if self.logger:
  1196. self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask))
  1197. elif suboption[1:2] == SET_MODEMSTATE_MASK:
  1198. self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number
  1199. if self.logger:
  1200. self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask))
  1201. elif suboption[1:2] == PURGE_DATA:
  1202. if suboption[2:3] == PURGE_RECEIVE_BUFFER:
  1203. self.serial.reset_input_buffer()
  1204. if self.logger:
  1205. self.logger.info("purge in")
  1206. self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER)
  1207. elif suboption[2:3] == PURGE_TRANSMIT_BUFFER:
  1208. self.serial.reset_output_buffer()
  1209. if self.logger:
  1210. self.logger.info("purge out")
  1211. self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER)
  1212. elif suboption[2:3] == PURGE_BOTH_BUFFERS:
  1213. self.serial.reset_input_buffer()
  1214. self.serial.reset_output_buffer()
  1215. if self.logger:
  1216. self.logger.info("purge both")
  1217. self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS)
  1218. else:
  1219. if self.logger:
  1220. self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:])))
  1221. else:
  1222. if self.logger:
  1223. self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:])))
  1224. else:
  1225. if self.logger:
  1226. self.logger.warning("unknown subnegotiation: {!r}".format(suboption))
  1227. # simple client test
  1228. if __name__ == '__main__':
  1229. import sys
  1230. s = Serial('rfc2217://localhost:7000', 115200)
  1231. sys.stdout.write('{}\n'.format(s))
  1232. sys.stdout.write("write...\n")
  1233. s.write(b"hello\n")
  1234. s.flush()
  1235. sys.stdout.write("read: {}\n".format(s.read(5)))
  1236. s.close()