예제 #1
0
 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")
예제 #2
0
    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
예제 #3
0
 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)
예제 #4
0
 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)
예제 #5
0
    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
예제 #6
0
 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
예제 #7
0
    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
예제 #8
0
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")
예제 #9
0
    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()