async def get_modem_state(self): """\ get last modem state (cached value. If value is "old", request a new one. This cache helps that we don't issue to many requests when e.g. all status lines, one after the other is queried by the user (CTS, DSR etc.) """ # active modem state polling enabled? is the value fresh enough? if self._poll_modem_state and self._modemstate_timeout.expired(): self.logger.debug('polling modem state') # when it is older, request an update await self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) timeout = Timeout(self._network_timeout) while not timeout.expired(): await asyncio.sleep(0.05) # prevent 100% CPU load # when expiration time is updated, it means that there is a new # value if not self._modemstate_timeout.expired(): break else: self.logger.warning('poll for modem state failed') # even when there is a timeout, do not generate an error just # return the last known value. this way we can support buggy # servers that do not respond to polls, but send automatic # updates. if self._modemstate is not None: self.logger.debug('using cached modem state') return self._modemstate else: # never received a notification from the server raise SerialException("remote sends no NOTIFY_MODEMSTATE")
def WaitForRespond(self, rxLen, timeout): timeout = Timeout(timeout) read_buf = b'' state = RECV_HEAD while not timeout.expired(): buf = self.ser.read(1024) if debug: print('RX1: ', binascii.b2a_hex(buf, b' ')) if state == RECV_HEAD: while buf: pos = buf.find(b'\x04') if pos < 0: buf = None break buf = buf[pos:] if len(buf) >= 7: # minimal len if buf[0:2] == b'\x04\x0e' and buf[ 3: 6] == b'\x01\xe0\xfc': # maybe a valid rx packet read_buf += buf state = RECV_BODY break buf = buf[1:] # forward to next if state == RECV_BODY: break read_buf = buf if debug and read_buf: print("RDBUF:", read_buf) return read_buf
def __init__(self, *args, **kwargs): self._socket = None self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) self._remote_suspend_flow = False self._write_lock = None self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 1 self._telnet_options = None self._rfc2217_port_settings = None self._rfc2217_options = None self._read_buffer = None super(Serial, self).__init__(*args, **kwargs)
async def read_until(self, terminator=b'\r', size=None, timeout=None): result = bytearray() to = Timeout(timeout) while True: c = self.read(1) if c: result += c if terminator in result: break if size is not None and len(result) >= size: break to.restart(timeout) if to.expired(): raise SerialTimeoutException('Read timeout') await asyncio.sleep(0) return bytes(result)
def _Do_Boot_ProtectFlash(self, mid: int, unprotect: bool): # 1. find flash info flash_info = GetFlashInfo(mid) if flash_info is None: return -1 if debug: print(flash_info.icNam, flash_info.manName) timeout = Timeout(1) # 2. write (un)protect word cw = flash_info.cwUnp if unprotect else flash_info.cwEnp while True: sr = 0 # read sr register for i in range(flash_info.szSR): f = self.bootItf.ReadFlashSR(flash_info.cwdRd[i]) if f[0]: sr |= f[2] << (8 * i) if debug: print("sr: {:x}".format(sr)) if debug: print("final sr: {:x}".format(sr)) print("msk: {:x}".format(flash_info.cwMsk)) print("cw: {:x}, sb: {}, lb: {}".format( cw, flash_info.sb, flash_info.lb)) print("bfd: {:x}".format(BFD(cw, flash_info.sb, flash_info.lb))) # if (un)protect word is set if (sr & flash_info.cwMsk) == BFD(cw, flash_info.sb, flash_info.lb): return 1 if timeout.expired(): return -2 # // set (un)protect word srt = sr & (flash_info.cwMsk ^ 0xffffffff) srt |= BFD(cw, flash_info.sb, flash_info.lb) f = self.bootItf.WriteFlashSR(flash_info.szSR, flash_info.cwdWr[0], srt & 0xffff) time.sleep(0.01) return 1
async def _read(self, size=1, timeout=None): result = b'' to = Timeout(timeout) n = 0 while len(result) < size: n += 1 r = self.com.s.read(1) #r = await self.com.read(1) if self.command.startswith(b'PV '): self.logger.debug('OOOOOOOOOOOO') if len(r) > 0: result += r to.restart(timeout) else: if to.expired(): self.logger.debug('Read timeout') raise SerialTimeoutException('Read timeout') await asyncio.sleep(0) self.logger.debug('%d', n) return result
async def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 1 if self._port is None: raise SerialException( "Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") host, port = self.from_url(self._port) try: self._socket = sockio.aio.TCP(host, port, auto_reconnect=False) await self._socket.open() except Exception as msg: self._socket = None raise SerialException( "Could not open port {}: {}".format( self.port, msg)) # use a thread save queue as buffer. it also simplifies implementing # the read timeout self._read_buffer = asyncio.Queue() # to ensure that user writes does not interfere with internal # telnet/rfc2217 options establish a lock self._write_lock = asyncio.Lock() mandadory_done = asyncio.Event() def on_mandadory_event(opt): if sum(o.active for o in mandadory_options) == sum( o.state != INACTIVE for o in mandadory_options): mandadory_done.set() # name the following separately so that, below, a check can be easily # done mandadory_options = [ TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE, option_changed_callback=on_mandadory_event), TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, option_changed_callback=on_mandadory_event), ] # all supported telnet options self._telnet_options = [ TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), TelnetOption( self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), TelnetOption( self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), TelnetOption( self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), ] + mandadory_options # RFC 2217 specific states # COM port settings self._rfc2217_port_settings = { 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), } # There are more subnegotiation objects, combine all in one dictionary # for easy access self._rfc2217_options = { 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), } self._rfc2217_options.update(self._rfc2217_port_settings) # cache for line and modem states that the server sends to us self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) # RFC 2217 flow control between server and client self._remote_suspend_flow = False self.is_open = True self._thread = asyncio.create_task(self._telnet_read_loop()) try: # must clean-up if open fails # negotiate Telnet/RFC 2217 -> send initial requests opts = [(option.send_yes, option.option) for option in self._telnet_options if option.state is REQUESTED] await self.telnet_send_options(opts) # now wait until important options are negotiated try: await asyncio.wait_for(mandadory_done.wait(), self._network_timeout) except asyncio.TimeoutError: raise SerialException( "Remote does not seem to support RFC2217 or BINARY mode {!r}" .format(mandadory_options)) self.logger.info( "Negotiated options: {}".format( self._telnet_options)) tasks = [] # fine, go on, set RFC 2271 specific things await self._reconfigure_port() # all things set up get, now a clean start if not self._dsrdtr: await self._update_dtr_state() if not self._rtscts: await self._update_rts_state() await self.reset_input_buffer() await self.reset_output_buffer() except BaseException: await self.close() raise
class Serial(SerialBase): BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200) def __init__(self, *args, **kwargs): self._socket = None self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) self._remote_suspend_flow = False self._write_lock = None self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 1 self._telnet_options = None self._rfc2217_port_settings = None self._rfc2217_options = None self._read_buffer = None super(Serial, self).__init__(*args, **kwargs) async def open(self): """\ Open port with current settings. This may throw a SerialException if the port cannot be opened. """ self._ignore_set_control_answer = False self._poll_modem_state = False self._network_timeout = 1 if self._port is None: raise SerialException( "Port must be configured before it can be used.") if self.is_open: raise SerialException("Port is already open.") host, port = self.from_url(self._port) try: self._socket = sockio.aio.TCP(host, port, auto_reconnect=False) await self._socket.open() except Exception as msg: self._socket = None raise SerialException( "Could not open port {}: {}".format( self.port, msg)) # use a thread save queue as buffer. it also simplifies implementing # the read timeout self._read_buffer = asyncio.Queue() # to ensure that user writes does not interfere with internal # telnet/rfc2217 options establish a lock self._write_lock = asyncio.Lock() mandadory_done = asyncio.Event() def on_mandadory_event(opt): if sum(o.active for o in mandadory_options) == sum( o.state != INACTIVE for o in mandadory_options): mandadory_done.set() # name the following separately so that, below, a check can be easily # done mandadory_options = [ TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE, option_changed_callback=on_mandadory_event), TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, option_changed_callback=on_mandadory_event), ] # all supported telnet options self._telnet_options = [ TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), TelnetOption( self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), TelnetOption( self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), TelnetOption( self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), ] + mandadory_options # RFC 2217 specific states # COM port settings self._rfc2217_port_settings = { 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), } # There are more subnegotiation objects, combine all in one dictionary # for easy access self._rfc2217_options = { 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), } self._rfc2217_options.update(self._rfc2217_port_settings) # cache for line and modem states that the server sends to us self._linestate = 0 self._modemstate = None self._modemstate_timeout = Timeout(-1) # RFC 2217 flow control between server and client self._remote_suspend_flow = False self.is_open = True self._thread = asyncio.create_task(self._telnet_read_loop()) try: # must clean-up if open fails # negotiate Telnet/RFC 2217 -> send initial requests opts = [(option.send_yes, option.option) for option in self._telnet_options if option.state is REQUESTED] await self.telnet_send_options(opts) # now wait until important options are negotiated try: await asyncio.wait_for(mandadory_done.wait(), self._network_timeout) except asyncio.TimeoutError: raise SerialException( "Remote does not seem to support RFC2217 or BINARY mode {!r}" .format(mandadory_options)) self.logger.info( "Negotiated options: {}".format( self._telnet_options)) tasks = [] # fine, go on, set RFC 2271 specific things await self._reconfigure_port() # all things set up get, now a clean start if not self._dsrdtr: await self._update_dtr_state() if not self._rtscts: await self._update_rts_state() await self.reset_input_buffer() await self.reset_output_buffer() except BaseException: await self.close() raise async def _reconfigure_port(self): """Set communication parameters on opened port.""" if self._socket is None: raise SerialException("Can only operate on open ports") # Setup the connection # to get good performance, all parameter changes are sent first... if not 0 < self._baudrate < 2 ** 32: raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) await self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) await self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) await self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) await self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) # and now wait until parameters are active items = list(self._rfc2217_port_settings.values()) self.logger.debug("Negotiating settings: {}".format(items)) async def wait(): for o in items: await o.active_event.wait() try: await asyncio.wait_for(wait(), self._network_timeout) except asyncio.TimeoutError: raise SerialException( "Remote does not accept parameter change (RFC2217): {!r}".format(items)) self.logger.info("Negotiated settings: {}".format(items)) if self._rtscts and self._xonxoff: raise ValueError('xonxoff and rtscts together are not supported') elif self._rtscts: await self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) elif self._xonxoff: await self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) else: await self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) async def close(self): """Close port""" self.is_open = False if self._socket: try: await self._socket.close() except BaseException: # ignore errors. pass if self._thread: # XXX more than socket timeout await asyncio.wait_for(self._thread, 7) self._thread = None # in case of quick reconnects, give the server some time await asyncio.sleep(0.3) self._socket = None def from_url(self, url): """\ extract host and port from an URL string, other settings are extracted an stored in instance """ parts = urllib.parse.urlsplit(url) if parts.scheme != "rfc2217": raise SerialException( 'expected a string in the form ' '"rfc2217://<host>:<port>[?option[&option...]]": ' 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) try: # process options now, directly altering self for option, values in urllib.parse.parse_qs( parts.query, True).items(): if option == 'logging': self.logger.setLevel(LOGGER_LEVELS[values[0]]) self.logger.debug('enabled logging') elif option == 'ign_set_control': self._ignore_set_control_answer = True elif option == 'poll_modem': self._poll_modem_state = True elif option == 'timeout': self._network_timeout = float(values[0]) else: raise ValueError('unknown option: {!r}'.format(option)) if not 0 <= parts.port < 65536: raise ValueError("port not in range 0...65535") except ValueError as e: raise SerialException( 'expected a string in the form ' '"rfc2217://<host>:<port>[?option[&option...]]": {}'.format(e)) return (parts.hostname, parts.port) # - - - - - - - - - - - - - - - - - - - - - - - - @property @ensure_open def in_waiting(self): """Return the number of bytes currently in the input buffer.""" return not self._read_buffer.empty() async def read(self, size=1): """\ Read size bytes from the serial port. It will block until the requested number of bytes is read. """ return await self._read(size=size) @ensure_open async def _read(self, size=1): data = bytearray() while len(data) < size: if self._thread is None or self._thread.done(): raise SerialException('connection failed (reader thread died)') buf = await self._read_buffer.get() if buf is None: break data += buf return bytes(data) @ensure_open async def write(self, data): """\ Output the given byte string over the serial port. Can block if the connection is blocked. May raise SerialException if the connection is closed. """ try: await self._internal_raw_write(to_bytes(data).replace(IAC, IAC_DOUBLED)) except socket.error as e: raise SerialException( "connection failed (socket error): {}".format(e)) return len(data) @ensure_open async def reset_input_buffer(self): """Clear input buffer, discarding all that is in the buffer.""" await self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) # empty read buffer while self._read_buffer.qsize(): self._read_buffer.get(False) @ensure_open async def reset_output_buffer(self): """\ Clear output buffer, aborting the current output and discarding all that is in the buffer. """ await self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) @ensure_open async def _update_break_state(self): """\ Set break: Controls TXD. When active, to transmitting is possible. """ self.logger.info( 'set BREAK to {}'.format( 'active' if self._break_state else 'inactive')) if self._break_state: await self.rfc2217_set_control(SET_CONTROL_BREAK_ON) else: await self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) @ensure_open async def _update_rts_state(self): """Set terminal status line: Request To Send.""" self.logger.info( 'set RTS to {}'.format( 'active' if self._rts_state else 'inactive')) if self._rts_state: await self.rfc2217_set_control(SET_CONTROL_RTS_ON) else: await self.rfc2217_set_control(SET_CONTROL_RTS_OFF) @ensure_open async def _update_dtr_state(self): """Set terminal status line: Data Terminal Ready.""" self.logger.info( 'set DTR to {}'.format( 'active' if self._dtr_state else 'inactive')) if self._dtr_state: await self.rfc2217_set_control(SET_CONTROL_DTR_ON) else: await self.rfc2217_set_control(SET_CONTROL_DTR_OFF) @property @ensure_open async def cts(self): """Read terminal status line: Clear To Send.""" return bool(await self.get_modem_state() & MODEMSTATE_MASK_CTS) @property @ensure_open async def dsr(self): """Read terminal status line: Data Set Ready.""" return bool(await self.get_modem_state() & MODEMSTATE_MASK_DSR) @property @ensure_open async def ri(self): """Read terminal status line: Ring Indicator.""" return bool(await self.get_modem_state() & MODEMSTATE_MASK_RI) @property @ensure_open async def cd(self): """Read terminal status line: Carrier Detect.""" return bool(await self.get_modem_state() & MODEMSTATE_MASK_CD) # - - - RFC2217 specific - - - async def _telnet_read_loop(self): """Read loop for the socket.""" mode = M_NORMAL suboption = None try: while self.is_open: try: data = await self._socket.read(1024) except socket.timeout: # just need to get out of recv form time to time to check if # still alive continue except socket.error as e: # connection fails -> terminate loop self.logger.debug( "socket error in reader thread: {}".format(e)) await self._read_buffer.put(None) break self.logger.debug('RECV %r', data) if not data: await self._read_buffer.put(None) break # lost connection for byte in iterbytes(data): if mode == M_NORMAL: # interpret as command or as data if byte == IAC: mode = M_IAC_SEEN else: # store data in read buffer or sub option buffer # depending on state if suboption is not None: suboption += byte else: await self._read_buffer.put(byte) elif mode == M_IAC_SEEN: if byte == IAC: # interpret as command doubled -> insert character # itself if suboption is not None: suboption += IAC else: await self._read_buffer.put(IAC) mode = M_NORMAL elif byte == SB: # sub option start suboption = bytearray() mode = M_NORMAL elif byte == SE: # sub option end -> process it now self._telnet_process_subnegotiation( bytes(suboption)) suboption = None mode = M_NORMAL elif byte in (DO, DONT, WILL, WONT): # negotiation telnet_command = byte mode = M_NEGOTIATE else: # other telnet commands self._telnet_process_command(byte) mode = M_NORMAL elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following await self._telnet_negotiate_option(telnet_command, byte) mode = M_NORMAL finally: self._thread = None self.logger.debug("read thread terminated") # - incoming telnet commands and options def _telnet_process_command(self, command): """Process commands other than DO, DONT, WILL, WONT.""" # Currently none. RFC2217 only uses negotiation and subnegotiation. self.logger.warning("ignoring Telnet command: {!r}".format(command)) async def _telnet_negotiate_option(self, command, option): """Process incoming DO, DONT, WILL, WONT.""" # check our registered telnet options and forward command to them # they know themselves if they have to answer or not known = False for item in self._telnet_options: # can have more than one match! as some options are duplicated for # 'us' and 'them' if item.option == option: await item.process_incoming(command) known = True if not known: # handle unknown options # only answer to positive requests and deny them if command == WILL or command == DO: await self.telnet_send_option((DONT if command == WILL else WONT), option) self.logger.warning( "rejected Telnet option: {!r}".format(option)) def _telnet_process_subnegotiation(self, suboption): """Process subnegotiation, the data between IAC SB and IAC SE.""" if suboption[0:1] == COM_PORT_OPTION: option = suboption[1:2] if option == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: self._linestate = ord(suboption[2:3]) # ensure it is a number self.logger.info( "NOTIFY_LINESTATE: {}".format( self._linestate)) elif option == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: self._modemstate = ord(suboption[2:3]) # ensure it is a number self.logger.info( "NOTIFY_MODEMSTATE: {}".format( self._modemstate)) # update time when we think that a poll would make sense self._modemstate_timeout.restart(0.3) elif option == FLOWCONTROL_SUSPEND: self._remote_suspend_flow = True elif option == FLOWCONTROL_RESUME: self._remote_suspend_flow = False else: for item in self._rfc2217_options.values(): if item.ack_option == option: # ~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) item.check_answer(bytes(suboption[2:])) break else: self.logger.warning( "ignoring COM_PORT_OPTION: {!r}".format(suboption)) else: self.logger.warning( "ignoring subnegotiation: {!r}".format(suboption)) # - outgoing telnet commands and options async def _internal_raw_write(self, data): """internal socket write with no data escaping. used to send telnet stuff.""" async with self._write_lock: self.logger.debug('SEND %r', data) await self._socket.write(data) async def telnet_send_option(self, action, option): """Send DO, DONT, WILL, WONT.""" await self._internal_raw_write(IAC + action + option) async def telnet_send_options(self, action_options): """Send DO, DONT, WILL, WONT.""" data = [] for action, option in action_options: data += IAC, action, option await self._internal_raw_write(b''.join(data)) async def rfc2217_send_subnegotiation(self, option, value=b''): """Subnegotiation of RFC2217 parameters.""" value = value.replace(IAC, IAC_DOUBLED) await self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) async def rfc2217_send_purge(self, value): """\ Send purge request to the remote. (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) """ item = self._rfc2217_options['purge'] await item.set(value) # transmit desired purge type # wait for acknowledge from the server await asyncio.wait_for(item.wait(), self._network_timeout) async def rfc2217_set_control(self, value): """transmit change of control line to remote""" item = self._rfc2217_options['control'] await item.set(value) # transmit desired control type if self._ignore_set_control_answer: # answers are ignored when option is set. compatibility mode for # servers that answer, but not the expected one... (or no answer # at all) i.e. sredird # this helps getting the unit tests passed await asyncio.sleep(0.1) else: # wait for acknowledge from the server await asyncio.wait_for(item.wait(), self._network_timeout) def rfc2217_flow_server_ready(self): """\ check if server is ready to receive data. block for some time when not. """ # ~ if self._remote_suspend_flow: # ~ wait--- async def get_modem_state(self): """\ get last modem state (cached value. If value is "old", request a new one. This cache helps that we don't issue to many requests when e.g. all status lines, one after the other is queried by the user (CTS, DSR etc.) """ # active modem state polling enabled? is the value fresh enough? if self._poll_modem_state and self._modemstate_timeout.expired(): self.logger.debug('polling modem state') # when it is older, request an update await self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) timeout = Timeout(self._network_timeout) while not timeout.expired(): await asyncio.sleep(0.05) # prevent 100% CPU load # when expiration time is updated, it means that there is a new # value if not self._modemstate_timeout.expired(): break else: self.logger.warning('poll for modem state failed') # even when there is a timeout, do not generate an error just # return the last known value. this way we can support buggy # servers that do not respond to polls, but send automatic # updates. if self._modemstate is not None: self.logger.debug('using cached modem state') return self._modemstate else: # never received a notification from the server raise SerialException("remote sends no NOTIFY_MODEMSTATE")
def programm(self, filename, startAddr=0x11000): # Step1: read file into system memory # TODO: sanity check with open(filename, "rb") as f: pfile = f.read() fileLen = filOLen = len(pfile) padding_len = 0x100 - (fileLen & 0xff) if padding_len: pfile += b'\xff' * padding_len fileLen += padding_len filOLen += padding_len # print("fileLen{}, padding_len {}".format(fileLen, padding_len)) # print("FILE: ", binascii.b2a_hex(pfile, b' ')) fileCrc = crc32_ver2(0xffffffff, pfile) total_num = filOLen // 0x1000 # do_reset_signal() # reboot = "reboot" # self.bootItf.Start_Cmd(reboot) # time.sleep(0.1) self.pbar = tqdm(total=total_num, ncols=80) self.log("Getting Bus...") timeout = Timeout(10) # Step2: Link Check while True: r = self.bootItf.LinkCheck() if r: break if timeout.expired(): self.log('Cannot get bus.') self.pbar.close() return # self.bootItf.Start_Cmd(reboot) # time.sleep(0.01) self.log("Gotten Bus...") self.bootItf.Drain() # Step3: set baudrate, delay 100ms if self.target_baudrate != 115200: if not self.bootItf.SetBR(self.target_baudrate, 50): self.log("Set baudrate failed") self.pbar.close() return self.log("Set baudrate successful") if self.unprotect: # Get Mid mid = self.bootItf.GetFlashMID() # print("mid: {:x}".format(mid)) self._Do_Boot_ProtectFlash(mid, True) # unprotect flash first # Step4: erase # Step4.1: read first 4k if startAddr not aligned with 4K eraseAddr = startAddr ss = s0 = eraseAddr & 0xfffff000 # 4K对齐的地址 filSPtr = 0 if eraseAddr & 0xfff: self.log("Aligning Flash Address...") # Read 4K from flash buf = self.bootItf.ReadSector(s0) l = 0x1000 - (eraseAddr & 0xfff) fl = l if l < fileLen else fileLen buf[eraseAddr & 0xfff:eraseAddr & 0xfff + fl] = pfile[:fl] self.bootItf.EraseBlock(0x20, s0) if not self.bootItf.WriteSector(s0, buf): self.log("WriteSector Failed") self.pbar.close() return filOLen -= fl filSPtr = fl s0 += 0x1000 ss = s0 if filOLen <= 0: # TODO: goto crc check return # Step4.2: handle the last 4K # now ss is the new eraseAddr. # 文件结束地址 filEPtr = fileLen s1 = eraseAddr + fileLen # If not 4K aligned, read the last sector, fill the data, write it back if s1 & 0xfff: # print("Handle file end...") buf = bytearray(b'\xff' * 4096) # fill with 0xFF buf[:s1 & 0xfff] = pfile[filEPtr - (s1 & 0xfff):] # copy s1&0xfff len # print(time.time()) for _ in range(4): if self.bootItf.EraseBlock(0x20, s1 & 0xfffff000): break time.sleep(0.05) # time.sleep(0.1) # for _ in range(10): # tt = self.bootItf.Read() # print(tt) #print(time.time()) if not self.bootItf.WriteSector(s1 & 0xfffff000, buf): self.log("WriteSector Failed") self.pbar.close() return # print(time.time()) filEPtr = filEPtr - (s1 & 0xfff) filOLen = filOLen - (s1 & 0xfff) if filOLen <= 0: # TODO: goto crc check pass # print("Handle file end done") # Step4.3: 对齐64KB,如果擦除区域小于64KB len1 = ss + filOLen len0 = s0 & 0xffff0000 if s0 & 0xffff: len0 += 0x10000 if filOLen > len0 - s0: while s0 < len0: self.bootItf.EraseBlock(0x20, s0) s0 += 0x1000 # print("fileOLen: {}, filSPtr: {}".format(filOLen, filSPtr)) # Step4.4: Erase 64k, 4k, etc. # 按照64KB/4K block擦除 len1 = ss + filOLen while s0 < len1: self.log("Erasing") rmn = len1 - s0 if rmn > 0x10000: # 64K erase self.bootItf.EraseBlock(0xd8, s0) s0 = s0 + 0x10000 else: # erase 4K self.bootItf.EraseBlock(0x20, s0) s0 = s0 + 0x1000 # Step5: Write data i = 0 while i < filOLen: self.log("Writing") for _ in range(4): if self.bootItf.WriteSector( ss + i, pfile[filSPtr + i:filSPtr + i + 4 * 1024]): self.pbar.update(1) break else: self.log("WriteSector 1 Failed") self.pbar.close() return i += 0x1000 # Step6: CRC check self.log("Verifing") if fileLen & 0xff: l2 = (fileLen & ~0xff) + 0x100 else: l2 = fileLen ret, crc = self.bootItf.ReadCRC(startAddr, startAddr + l2 - 1) if not ret: self.log("Read CRC Failed") self.pbar.close() return if crc != fileCrc: self.log("CRC not equal") self.pbar.close() return if self.unprotect: self._Do_Boot_ProtectFlash(mid, False) self.log("Write Successful") self.pbar.close()