Ejemplo n.º 1
0
    def _read_frame_header(self,
                           expected_frame_type: int = None) -> Tuple[int, int]:
        """Read frame header and frame type. Return them as tuple of integers.

        :param expected_frame_type: Check if the frame_type is exactly as expected
        :return: Tuple of integers representing frame header and frame type
        :raises McuBootDataAbortError: Target sens Data Abort frame
        :raises McuBootConnectionError: Unexpected frame header or frame type (if specified)
        :raises McuBootConnectionError: When received invalid ACK
        """
        timeout = Timeout(self.timeout, "ms")
        while not timeout.overflow():
            header = to_int(self._read(1))
            if header != self.FRAME_START_BYTE_NOT_READY:
                break

        if header != self.FRAME_START_BYTE:
            raise McuBootConnectionError(
                f"Received invalid frame header '{header:#X}' expected '{self.FRAME_START_BYTE:#X}'"
            )
        frame_type = to_int(self._read(1))
        if frame_type == FPType.ABORT:
            raise McuBootDataAbortError()
        if expected_frame_type:
            if frame_type != expected_frame_type:
                raise McuBootConnectionError(
                    f"received invalid ACK '{frame_type:#X}' expected '{expected_frame_type:#X}'"
                )
        return header, frame_type
Ejemplo n.º 2
0
    def ping(self) -> None:
        """Ping the target device, retrieve protocol version.

        :raises McuBootConnectionError: If the target device doesn't respond to ping
        :raises McuBootConnectionError: If the start frame is not received
        :raises McuBootConnectionError: If the header is invalid
        :raises McuBootConnectionError: If the frame type is invalid
        :raises McuBootConnectionError: If the ping response is not received
        :raises McuBootConnectionError: If crc does not match
        """
        with self.ping_timeout(timeout=PING_TIMEOUT_MS):
            ping = struct.pack("<BB", self.FRAME_START_BYTE, FPType.PING)
            self._send_frame(ping, wait_for_ack=False)
            # after power cycle, MBoot v 3.0+ may respond to first command with a leading dummy data
            # we read data from UART until the FRAME_START_BYTE byte
            start_byte = b""
            for i in range(MAX_PING_RESPONSE_DUMMY_BYTES):
                start_byte = self._read(1)
                if start_byte is None:
                    raise McuBootConnectionError(
                        "Failed to receive initial byte")

                if start_byte == self.FRAME_START_BYTE.to_bytes(
                        length=1, byteorder="little"):
                    logger.debug(
                        f"FRAME_START_BYTE received in {i + 1}. attempt.")
                    break
            else:
                raise McuBootConnectionError(
                    "Failed to receive FRAME_START_BYTE")

            header = to_int(start_byte)
            if header != self.FRAME_START_BYTE:
                raise McuBootConnectionError("Header is invalid")
            frame_type = to_int(self._read(1))
            if frame_type != FPType.PINGR:
                raise McuBootConnectionError("Frame type is invalid")

            response_data = self._read(8)
            response = PingResponse.parse(response_data)

            # ping response has different crc computation than the other responses
            # that's why we can't use calc_frame_crc method
            # crc data for ping excludes the last 2B of response data, which holds the CRC from device
            crc_data = struct.pack(f"<BB{len(response_data) -2}B", header,
                                   frame_type, *response_data[:-2])
            crc = calc_crc(crc_data)
            if crc != response.crc:
                raise McuBootConnectionError("Received CRC doesn't match")

            self.protocol_version = response.version
            self.options = response.options
Ejemplo n.º 3
0
    def close(self) -> None:
        """Close the UART interface.

        :raises McuBootConnectionError: In any case of fail of UART close operation.
        """
        try:
            self.device.close()
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
Ejemplo n.º 4
0
    def info(self) -> str:
        """Return information about the UART interface.

        :return: Description of UART interface
        :raises McuBootConnectionError: When no port is available
        """
        try:
            return self.device.port
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
Ejemplo n.º 5
0
    def open(self) -> None:
        """Open the UART interface.

        :raises McuBootConnectionError: In any case of fail of UART open operation.
        """
        for n in range(MAX_UART_OPEN_ATTEMPTS):
            try:
                self.device.open()
                self.ping()
                logger.debug(f"Interface opened after {n + 1} attempts.")
                return
            except (McuBootConnectionError, TimeoutError) as e:
                self.device.close()
                logger.debug(f"Opening interface failed with: {repr(e)}")
            except Exception as exc:
                self.device.close()
                raise McuBootConnectionError(
                    "UART Interface open operation fails.") from exc
        raise McuBootConnectionError(
            f"Cannot open UART interface after {MAX_UART_OPEN_ATTEMPTS} attempts."
        )
Ejemplo n.º 6
0
def test_catch_spsdk_error():
    with pytest.raises(SystemExit) as exc:
        function_under_test(AssertionError())
    assert exc.value.code == 2

    with pytest.raises(SystemExit) as exc_2:
        function_under_test(McuBootConnectionError())
    assert exc_2.value.code == 2

    with pytest.raises(SystemExit) as exc_3:
        function_under_test(IndexError())
    assert exc_3.value.code == 3

    assert function_under_test(None) == 0
Ejemplo n.º 7
0
    def _write(self, data: bytes) -> None:
        """Send data to device.

        :param data: Data to send
        :raises McuBootConnectionError: When sending the data fails
        """
        logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]")
        try:
            self.device.reset_input_buffer()
            self.device.reset_output_buffer()
            self.device.write(data)
            self.device.flush()
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
Ejemplo n.º 8
0
    def parse(cls, data: bytes) -> "PingResponse":
        """Parse raw data into PingResponse object.

        :param data: bytes to be unpacked to PingResponse object
            4B version, 2B data, 2B CRC16
        :raises McuBootConnectionError: Received invalid ping response
        :return: PingResponse
        """
        try:
            version, options, crc = struct.unpack("<I2H", data)
        except struct.error as err:
            raise McuBootConnectionError(
                "Received invalid ping response") from err
        return cls(version, options, crc)
Ejemplo n.º 9
0
    def _write(self, data: bytes) -> None:
        """Send data to device.

        :param data: Data to send
        :raises McuBootConnectionError: When sending the data fails
        :raises TimeoutError: When data NAKed or could not be written
        """
        logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]")
        try:
            result = self.port.DeviceWrite(devAddr=self.i2c_address,
                                           txData=data)
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
        if result < 0:
            raise TimeoutError()
Ejemplo n.º 10
0
    def _read(self, length: int) -> bytes:
        """Read 'length' amount for bytes from device.

        :param length: Number of bytes to read
        :return: Data read from the device
        :raises TimeoutError: Time-out
        :raises McuBootConnectionError: When reading data from device fails
        """
        try:
            data = self.device.read(length)
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
        if not data:
            raise TimeoutError()
        logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>")
        return data
Ejemplo n.º 11
0
    def _write(self, data: bytes) -> None:
        """Send data to device.

        :param data: Data to send
        :raises McuBootConnectionError: When sending the data fails
        :raises TimeoutError: When data could not be written
        """
        logger.debug(f"[{' '.join(f'{b:02x}' for b in data)}]")
        try:
            (dummy,
             result) = self.port.Transfer(devSelectPort=self.spi_sselport,
                                          devSelectPin=self.spi_sselpin,
                                          txData=data)
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
        if result < 0:
            raise TimeoutError()
Ejemplo n.º 12
0
    def _read(self, length: int) -> bytes:
        """Read 'length' amount for bytes from device.

        :param length: Number of bytes to read
        :return: Data read from the device
        :raises TimeoutError: Time-out
        :raises McuBootConnectionError: When reading data from device fails
        :raises TimeoutError: When no data received
        """
        try:
            (data, result) = self.port.DeviceRead(devAddr=self.i2c_address,
                                                  rxSize=length)
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
        if result < 0 or not data:
            raise TimeoutError()
        logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>")
        return data
Ejemplo n.º 13
0
    def ping(self) -> None:
        """Ping the target device, retrieve protocol version.

        :raises AssertionError: If the target device doesn't respond to ping
        :raises McuBootConnectionError: If the start frame is not received
        """
        ping = struct.pack('<BB', self.FRAME_START_BYTE, FPType.PING)
        self._send_frame(ping, wait_for_ack=False)

        # after power cycle, MBoot v 3.0+ may respond to first command with a leading dummy data
        # we read data from UART until the FRAME_START_BYTE byte
        start_byte = b''
        for i in range(MAX_PING_RESPONSE_DUMMY_BYTES):
            start_byte = self._read(1)
            assert start_byte, f"Failed to receive initial byte"

            if start_byte == self.FRAME_START_BYTE.to_bytes(
                    length=1, byteorder='little'):
                logger.debug(f"FRAME_START_BYTE received in {i + 1}. attempt.")
                break
        else:
            raise McuBootConnectionError(f"Failed to receive FRAME_START_BYTE")

        header = to_int(start_byte)
        assert header == self.FRAME_START_BYTE
        frame_type = to_int(self._read(1))
        assert frame_type == FPType.PINGR

        response_data = self._read(8)
        assert response_data, f"Failed to receive ping response"
        response = PING_RESPONSE.parse(response_data)

        # ping response has different crc computation than the other responses
        # that's why we can't use calc_frame_crc method
        # crc data for ping excludes the last 2B of response data, which holds the CRC from device
        crc_data = struct.pack(f'<BB{len(response_data) -2}B', header,
                               frame_type, *response_data[:-2])
        crc = calc_crc(crc_data)
        assert crc == response.crc, \
            f"Received CRC doesn't match"

        self.protocol_version = response.version
        self.options = response.options
Ejemplo n.º 14
0
    def read(self) -> Union[CmdResponse, bytes]:
        """Read data from device.

        :return: read data
        :raises McuBootDataAbortError: Indicates data transmission abort
        :raises McuBootConnectionError: When received invalid CRC
        """
        _, frame_type = self._read_frame_header()
        length = to_int(self._read(2))
        crc = to_int(self._read(2))
        if not length:
            self._send_ack()
            raise McuBootDataAbortError()
        data = self._read(length)
        self._send_ack()
        calculated_crc = self._calc_frame_crc(data, frame_type)
        if crc != calculated_crc:
            raise McuBootConnectionError("Received invalid CRC")
        if frame_type == FPType.CMD:
            return parse_cmd_response(data)
        return data
Ejemplo n.º 15
0
    def _read(self, length: int) -> bytes:
        """Read 'length' amount for bytes from device.

        :param length: Number of bytes to read
        :return: Data read from the device
        :raises McuBootConnectionError: When reading data from device fails
        :raises TimeoutError: When no data received
        """
        try:
            (data, result) = self.port.Transfer(
                devSelectPort=self.spi_sselport,
                devSelectPin=self.spi_sselpin,
                txData=None,
                size=length,
            )
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e
        if result < 0 or not data:
            raise TimeoutError()
        logger.debug(f"<{' '.join(f'{b:02x}' for b in data)}>")
        return data
Ejemplo n.º 16
0
    def __init__(self,
                 port: str = None,
                 baudrate: int = 57600,
                 timeout: int = 5000) -> None:
        """Initialize the UART interface.

        :param port: name of the serial port, defaults to None
        :param baudrate: baudrate of the serial port, defaults to 57600
        :param timeout: read/write timeout in milliseconds, defaults to 5000
        :raises McuBootConnectionError: when the port could not be opened
        """
        super().__init__()
        try:
            self.timeout = timeout
            self.device = Serial(port=port,
                                 timeout=timeout / 1000,
                                 baudrate=baudrate)
            self.close()
            self.protocol_version = 0
            self.options = 0
        except Exception as e:
            raise McuBootConnectionError(str(e)) from e