rfc2217.py 58 KB

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