Repostiory containing DAPLink source code with Reset Pin workaround for HANI_IOT board.
Upstream: https://github.com/ARMmbed/DAPLink
test/daplink_board.py@0:01f31e923fe2, 2020-04-07 (annotated)
- Committer:
- Pawel Zarembski
- Date:
- Tue Apr 07 12:55:42 2020 +0200
- Revision:
- 0:01f31e923fe2
hani: DAPLink with reset workaround
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Pawel Zarembski |
0:01f31e923fe2 | 1 | # |
Pawel Zarembski |
0:01f31e923fe2 | 2 | # DAPLink Interface Firmware |
Pawel Zarembski |
0:01f31e923fe2 | 3 | # Copyright (c) 2009-2016, ARM Limited, All Rights Reserved |
Pawel Zarembski |
0:01f31e923fe2 | 4 | # SPDX-License-Identifier: Apache-2.0 |
Pawel Zarembski |
0:01f31e923fe2 | 5 | # |
Pawel Zarembski |
0:01f31e923fe2 | 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may |
Pawel Zarembski |
0:01f31e923fe2 | 7 | # not use this file except in compliance with the License. |
Pawel Zarembski |
0:01f31e923fe2 | 8 | # You may obtain a copy of the License at |
Pawel Zarembski |
0:01f31e923fe2 | 9 | # |
Pawel Zarembski |
0:01f31e923fe2 | 10 | # http://www.apache.org/licenses/LICENSE-2.0 |
Pawel Zarembski |
0:01f31e923fe2 | 11 | # |
Pawel Zarembski |
0:01f31e923fe2 | 12 | # Unless required by applicable law or agreed to in writing, software |
Pawel Zarembski |
0:01f31e923fe2 | 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
Pawel Zarembski |
0:01f31e923fe2 | 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
Pawel Zarembski |
0:01f31e923fe2 | 15 | # See the License for the specific language governing permissions and |
Pawel Zarembski |
0:01f31e923fe2 | 16 | # limitations under the License. |
Pawel Zarembski |
0:01f31e923fe2 | 17 | # |
Pawel Zarembski |
0:01f31e923fe2 | 18 | |
Pawel Zarembski |
0:01f31e923fe2 | 19 | from __future__ import absolute_import |
Pawel Zarembski |
0:01f31e923fe2 | 20 | |
Pawel Zarembski |
0:01f31e923fe2 | 21 | import os |
Pawel Zarembski |
0:01f31e923fe2 | 22 | import re |
Pawel Zarembski |
0:01f31e923fe2 | 23 | import time |
Pawel Zarembski |
0:01f31e923fe2 | 24 | import subprocess |
Pawel Zarembski |
0:01f31e923fe2 | 25 | import sys |
Pawel Zarembski |
0:01f31e923fe2 | 26 | import binascii |
Pawel Zarembski |
0:01f31e923fe2 | 27 | import itertools |
Pawel Zarembski |
0:01f31e923fe2 | 28 | import mbed_lstools |
Pawel Zarembski |
0:01f31e923fe2 | 29 | import info |
Pawel Zarembski |
0:01f31e923fe2 | 30 | import test_daplink |
Pawel Zarembski |
0:01f31e923fe2 | 31 | from test_info import TestInfoStub, TestInfo |
Pawel Zarembski |
0:01f31e923fe2 | 32 | from intelhex import IntelHex |
Pawel Zarembski |
0:01f31e923fe2 | 33 | from pyocd.core.helpers import ConnectHelper |
Pawel Zarembski |
0:01f31e923fe2 | 34 | |
Pawel Zarembski |
0:01f31e923fe2 | 35 | FILE_IGNORE_PATTERN_LIST = [ |
Pawel Zarembski |
0:01f31e923fe2 | 36 | re.compile("\\._\\.Trashes") |
Pawel Zarembski |
0:01f31e923fe2 | 37 | ] |
Pawel Zarembski |
0:01f31e923fe2 | 38 | |
Pawel Zarembski |
0:01f31e923fe2 | 39 | |
Pawel Zarembski |
0:01f31e923fe2 | 40 | # This prevents the following error message from getting |
Pawel Zarembski |
0:01f31e923fe2 | 41 | # displayed on windows if the mbed dismounts unexpectedly |
Pawel Zarembski |
0:01f31e923fe2 | 42 | # during a transfer: |
Pawel Zarembski |
0:01f31e923fe2 | 43 | # There is no disk in the drive. Please insert a disk into |
Pawel Zarembski |
0:01f31e923fe2 | 44 | # drive \Device\<Harddiskx>\<rdrive> |
Pawel Zarembski |
0:01f31e923fe2 | 45 | def disable_popup(): |
Pawel Zarembski |
0:01f31e923fe2 | 46 | if sys.platform.startswith("win"): |
Pawel Zarembski |
0:01f31e923fe2 | 47 | # pylint: disable=invalid-name |
Pawel Zarembski |
0:01f31e923fe2 | 48 | import ctypes |
Pawel Zarembski |
0:01f31e923fe2 | 49 | SEM_FAILCRITICALERRORS = 1 |
Pawel Zarembski |
0:01f31e923fe2 | 50 | GetErrorMode = \ |
Pawel Zarembski |
0:01f31e923fe2 | 51 | ctypes.windll.kernel32.GetErrorMode # @UndefinedVariable |
Pawel Zarembski |
0:01f31e923fe2 | 52 | GetErrorMode.restype = ctypes.c_uint |
Pawel Zarembski |
0:01f31e923fe2 | 53 | GetErrorMode.argtypes = [] |
Pawel Zarembski |
0:01f31e923fe2 | 54 | SetErrorMode = \ |
Pawel Zarembski |
0:01f31e923fe2 | 55 | ctypes.windll.kernel32.SetErrorMode # @UndefinedVariable |
Pawel Zarembski |
0:01f31e923fe2 | 56 | SetErrorMode.restype = ctypes.c_uint |
Pawel Zarembski |
0:01f31e923fe2 | 57 | SetErrorMode.argtypes = [ctypes.c_uint] |
Pawel Zarembski |
0:01f31e923fe2 | 58 | |
Pawel Zarembski |
0:01f31e923fe2 | 59 | err_mode = GetErrorMode() |
Pawel Zarembski |
0:01f31e923fe2 | 60 | err_mode |= SEM_FAILCRITICALERRORS |
Pawel Zarembski |
0:01f31e923fe2 | 61 | SetErrorMode(err_mode) |
Pawel Zarembski |
0:01f31e923fe2 | 62 | |
Pawel Zarembski |
0:01f31e923fe2 | 63 | disable_popup() |
Pawel Zarembski |
0:01f31e923fe2 | 64 | |
Pawel Zarembski |
0:01f31e923fe2 | 65 | |
Pawel Zarembski |
0:01f31e923fe2 | 66 | def get_all_attached_daplink_boards(): |
Pawel Zarembski |
0:01f31e923fe2 | 67 | all_boards = [] |
Pawel Zarembski |
0:01f31e923fe2 | 68 | lstools = mbed_lstools.create() |
Pawel Zarembski |
0:01f31e923fe2 | 69 | mbed_list = lstools.list_mbeds() |
Pawel Zarembski |
0:01f31e923fe2 | 70 | for mbed in mbed_list: |
Pawel Zarembski |
0:01f31e923fe2 | 71 | unique_id = mbed['target_id'] |
Pawel Zarembski |
0:01f31e923fe2 | 72 | board = DaplinkBoard(unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 73 | if board._mode is not None: #Valid daplink should have set this mode |
Pawel Zarembski |
0:01f31e923fe2 | 74 | all_boards.append(board) |
Pawel Zarembski |
0:01f31e923fe2 | 75 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 76 | print("Warning: DAPLink tests cannot be done on board %s" % board.unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 77 | return all_boards |
Pawel Zarembski |
0:01f31e923fe2 | 78 | |
Pawel Zarembski |
0:01f31e923fe2 | 79 | |
Pawel Zarembski |
0:01f31e923fe2 | 80 | def _unique_id_to_host_id(unique_id): |
Pawel Zarembski |
0:01f31e923fe2 | 81 | """Return the chip id unique to the daplink host procesor |
Pawel Zarembski |
0:01f31e923fe2 | 82 | |
Pawel Zarembski |
0:01f31e923fe2 | 83 | Unique ID has the following fomat |
Pawel Zarembski |
0:01f31e923fe2 | 84 | Board ID - 4 bytes |
Pawel Zarembski |
0:01f31e923fe2 | 85 | Version - 4 bytes |
Pawel Zarembski |
0:01f31e923fe2 | 86 | Host ID - Everything else |
Pawel Zarembski |
0:01f31e923fe2 | 87 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 88 | return unique_id[8:8 + 32] |
Pawel Zarembski |
0:01f31e923fe2 | 89 | |
Pawel Zarembski |
0:01f31e923fe2 | 90 | |
Pawel Zarembski |
0:01f31e923fe2 | 91 | def _get_board_endpoints(unique_id): |
Pawel Zarembski |
0:01f31e923fe2 | 92 | """Return a tuple of unique_id, serial_port, mount_point""" |
Pawel Zarembski |
0:01f31e923fe2 | 93 | lstools = mbed_lstools.create() |
Pawel Zarembski |
0:01f31e923fe2 | 94 | mbed_list = lstools.list_mbeds() |
Pawel Zarembski |
0:01f31e923fe2 | 95 | |
Pawel Zarembski |
0:01f31e923fe2 | 96 | host_id = _unique_id_to_host_id(unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 97 | for mbed in mbed_list: |
Pawel Zarembski |
0:01f31e923fe2 | 98 | mbed_unique_id = mbed['target_id'] |
Pawel Zarembski |
0:01f31e923fe2 | 99 | mbed_serial_port = mbed['serial_port'] |
Pawel Zarembski |
0:01f31e923fe2 | 100 | mbed_mount_point = mbed['mount_point'] |
Pawel Zarembski |
0:01f31e923fe2 | 101 | mbed_host_id = _unique_id_to_host_id(mbed_unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 102 | if mbed_host_id == host_id: |
Pawel Zarembski |
0:01f31e923fe2 | 103 | return mbed_unique_id, mbed_serial_port, mbed_mount_point |
Pawel Zarembski |
0:01f31e923fe2 | 104 | return None |
Pawel Zarembski |
0:01f31e923fe2 | 105 | |
Pawel Zarembski |
0:01f31e923fe2 | 106 | |
Pawel Zarembski |
0:01f31e923fe2 | 107 | def _ranges(i): |
Pawel Zarembski |
0:01f31e923fe2 | 108 | for _, b in itertools.groupby(enumerate(i), lambda x_y: x_y[1] - x_y[0]): |
Pawel Zarembski |
0:01f31e923fe2 | 109 | b = list(b) |
Pawel Zarembski |
0:01f31e923fe2 | 110 | yield b[0][1], b[-1][1] |
Pawel Zarembski |
0:01f31e923fe2 | 111 | |
Pawel Zarembski |
0:01f31e923fe2 | 112 | |
Pawel Zarembski |
0:01f31e923fe2 | 113 | def _parse_kvp_file(file_path, parent_test=None): |
Pawel Zarembski |
0:01f31e923fe2 | 114 | """Parse details.txt and return True if successful""" |
Pawel Zarembski |
0:01f31e923fe2 | 115 | test_info = None |
Pawel Zarembski |
0:01f31e923fe2 | 116 | kvp = {} |
Pawel Zarembski |
0:01f31e923fe2 | 117 | if parent_test is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 118 | test_info = parent_test.create_subtest('parse_kvp_file') |
Pawel Zarembski |
0:01f31e923fe2 | 119 | line_format = re.compile("^([a-zA-Z0-9 ]+): +(.+)$") |
Pawel Zarembski |
0:01f31e923fe2 | 120 | if not os.path.isfile(file_path): |
Pawel Zarembski |
0:01f31e923fe2 | 121 | return kvp |
Pawel Zarembski |
0:01f31e923fe2 | 122 | |
Pawel Zarembski |
0:01f31e923fe2 | 123 | with open(file_path, "r") as file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 124 | for line in file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 125 | if len(line) <= 0: |
Pawel Zarembski |
0:01f31e923fe2 | 126 | if test_info is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 127 | test_info.failure("Empty line in %s" % file_path) |
Pawel Zarembski |
0:01f31e923fe2 | 128 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 129 | |
Pawel Zarembski |
0:01f31e923fe2 | 130 | if line[0] == '#': |
Pawel Zarembski |
0:01f31e923fe2 | 131 | # The line is a comment |
Pawel Zarembski |
0:01f31e923fe2 | 132 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 133 | |
Pawel Zarembski |
0:01f31e923fe2 | 134 | match = line_format.match(line) |
Pawel Zarembski |
0:01f31e923fe2 | 135 | if match is None: |
Pawel Zarembski |
0:01f31e923fe2 | 136 | if test_info is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 137 | test_info.failure("Invalid line: %s" % line) |
Pawel Zarembski |
0:01f31e923fe2 | 138 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 139 | |
Pawel Zarembski |
0:01f31e923fe2 | 140 | key = match.group(1) |
Pawel Zarembski |
0:01f31e923fe2 | 141 | key = key.lower().replace(" ", "_") |
Pawel Zarembski |
0:01f31e923fe2 | 142 | value = match.group(2) |
Pawel Zarembski |
0:01f31e923fe2 | 143 | value = value.lower() |
Pawel Zarembski |
0:01f31e923fe2 | 144 | value = value.strip() |
Pawel Zarembski |
0:01f31e923fe2 | 145 | if key in kvp: |
Pawel Zarembski |
0:01f31e923fe2 | 146 | if test_info is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 147 | test_info.failure("Duplicate key %s" % key) |
Pawel Zarembski |
0:01f31e923fe2 | 148 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 149 | kvp[key] = value |
Pawel Zarembski |
0:01f31e923fe2 | 150 | return kvp |
Pawel Zarembski |
0:01f31e923fe2 | 151 | |
Pawel Zarembski |
0:01f31e923fe2 | 152 | |
Pawel Zarembski |
0:01f31e923fe2 | 153 | def _compute_crc(hex_file_path): |
Pawel Zarembski |
0:01f31e923fe2 | 154 | # Read in hex file |
Pawel Zarembski |
0:01f31e923fe2 | 155 | new_hex_file = IntelHex() |
Pawel Zarembski |
0:01f31e923fe2 | 156 | new_hex_file.padding = 0xFF |
Pawel Zarembski |
0:01f31e923fe2 | 157 | new_hex_file.fromfile(hex_file_path, format='hex') |
Pawel Zarembski |
0:01f31e923fe2 | 158 | |
Pawel Zarembski |
0:01f31e923fe2 | 159 | # Get the starting and ending address |
Pawel Zarembski |
0:01f31e923fe2 | 160 | addresses = new_hex_file.addresses() |
Pawel Zarembski |
0:01f31e923fe2 | 161 | addresses.sort() |
Pawel Zarembski |
0:01f31e923fe2 | 162 | start_end_pairs = list(_ranges(addresses)) |
Pawel Zarembski |
0:01f31e923fe2 | 163 | regions = len(start_end_pairs) |
Pawel Zarembski |
0:01f31e923fe2 | 164 | assert regions == 1, ("Error - only 1 region allowed in " |
Pawel Zarembski |
0:01f31e923fe2 | 165 | "hex file %i found." % regions) |
Pawel Zarembski |
0:01f31e923fe2 | 166 | start, end = start_end_pairs[0] |
Pawel Zarembski |
0:01f31e923fe2 | 167 | |
Pawel Zarembski |
0:01f31e923fe2 | 168 | # Compute checksum over the range (don't include data at location of crc) |
Pawel Zarembski |
0:01f31e923fe2 | 169 | size = end - start + 1 |
Pawel Zarembski |
0:01f31e923fe2 | 170 | crc_size = size - 4 |
Pawel Zarembski |
0:01f31e923fe2 | 171 | data = new_hex_file.tobinarray(start=start, size=crc_size) |
Pawel Zarembski |
0:01f31e923fe2 | 172 | data_crc32 = binascii.crc32(data) & 0xFFFFFFFF |
Pawel Zarembski |
0:01f31e923fe2 | 173 | |
Pawel Zarembski |
0:01f31e923fe2 | 174 | # Grab the crc from the image |
Pawel Zarembski |
0:01f31e923fe2 | 175 | embedded_crc32 = (((new_hex_file[end - 3] & 0xFF) << 0) | |
Pawel Zarembski |
0:01f31e923fe2 | 176 | ((new_hex_file[end - 2] & 0xFF) << 8) | |
Pawel Zarembski |
0:01f31e923fe2 | 177 | ((new_hex_file[end - 1] & 0xFF) << 16) | |
Pawel Zarembski |
0:01f31e923fe2 | 178 | ((new_hex_file[end - 0] & 0xFF) << 24)) |
Pawel Zarembski |
0:01f31e923fe2 | 179 | return data_crc32, embedded_crc32 |
Pawel Zarembski |
0:01f31e923fe2 | 180 | |
Pawel Zarembski |
0:01f31e923fe2 | 181 | |
Pawel Zarembski |
0:01f31e923fe2 | 182 | def _run_chkdsk(drive): |
Pawel Zarembski |
0:01f31e923fe2 | 183 | args = ["chkdsk", drive] |
Pawel Zarembski |
0:01f31e923fe2 | 184 | process = subprocess.Popen(args, stdin=subprocess.PIPE, |
Pawel Zarembski |
0:01f31e923fe2 | 185 | stdout=subprocess.PIPE, |
Pawel Zarembski |
0:01f31e923fe2 | 186 | stderr=subprocess.PIPE) |
Pawel Zarembski |
0:01f31e923fe2 | 187 | process.communicate(input=bytearray('n\r\n',encoding='ascii')) # Answer no if prompted |
Pawel Zarembski |
0:01f31e923fe2 | 188 | process.wait() |
Pawel Zarembski |
0:01f31e923fe2 | 189 | return process.returncode |
Pawel Zarembski |
0:01f31e923fe2 | 190 | |
Pawel Zarembski |
0:01f31e923fe2 | 191 | |
Pawel Zarembski |
0:01f31e923fe2 | 192 | class AssertInfo(object): |
Pawel Zarembski |
0:01f31e923fe2 | 193 | |
Pawel Zarembski |
0:01f31e923fe2 | 194 | def __init__(self, file_name, line_number): |
Pawel Zarembski |
0:01f31e923fe2 | 195 | self._file = file_name |
Pawel Zarembski |
0:01f31e923fe2 | 196 | self._line = line_number |
Pawel Zarembski |
0:01f31e923fe2 | 197 | |
Pawel Zarembski |
0:01f31e923fe2 | 198 | @property |
Pawel Zarembski |
0:01f31e923fe2 | 199 | def file(self): |
Pawel Zarembski |
0:01f31e923fe2 | 200 | return self._file |
Pawel Zarembski |
0:01f31e923fe2 | 201 | |
Pawel Zarembski |
0:01f31e923fe2 | 202 | @property |
Pawel Zarembski |
0:01f31e923fe2 | 203 | def line(self): |
Pawel Zarembski |
0:01f31e923fe2 | 204 | return self._line |
Pawel Zarembski |
0:01f31e923fe2 | 205 | |
Pawel Zarembski |
0:01f31e923fe2 | 206 | |
Pawel Zarembski |
0:01f31e923fe2 | 207 | class DaplinkBoard(object): |
Pawel Zarembski |
0:01f31e923fe2 | 208 | |
Pawel Zarembski |
0:01f31e923fe2 | 209 | MODE_IF = "interface" |
Pawel Zarembski |
0:01f31e923fe2 | 210 | MODE_BL = "bootloader" |
Pawel Zarembski |
0:01f31e923fe2 | 211 | |
Pawel Zarembski |
0:01f31e923fe2 | 212 | # Keys for details.txt |
Pawel Zarembski |
0:01f31e923fe2 | 213 | KEY_UNIQUE_ID = "unique_id" |
Pawel Zarembski |
0:01f31e923fe2 | 214 | KEY_HIC_ID = "hic_id" |
Pawel Zarembski |
0:01f31e923fe2 | 215 | KEY_MODE = "daplink_mode" |
Pawel Zarembski |
0:01f31e923fe2 | 216 | KEY_BL_VERSION = "bootloader_version" |
Pawel Zarembski |
0:01f31e923fe2 | 217 | KEY_IF_VERSION = "interface_version" |
Pawel Zarembski |
0:01f31e923fe2 | 218 | KEY_GIT_SHA = "git_sha" |
Pawel Zarembski |
0:01f31e923fe2 | 219 | KEY_LOCAL_MODS = "local_mods" |
Pawel Zarembski |
0:01f31e923fe2 | 220 | KEY_USB_INTERFACES = "usb_interfaces" |
Pawel Zarembski |
0:01f31e923fe2 | 221 | KEY_BL_CRC = "bootloader_crc" |
Pawel Zarembski |
0:01f31e923fe2 | 222 | KEY_IF_CRC = "interface_crc" |
Pawel Zarembski |
0:01f31e923fe2 | 223 | KEY_REMOUNT_COUNT = "remount_count" |
Pawel Zarembski |
0:01f31e923fe2 | 224 | |
Pawel Zarembski |
0:01f31e923fe2 | 225 | def __init__(self, unique_id): |
Pawel Zarembski |
0:01f31e923fe2 | 226 | |
Pawel Zarembski |
0:01f31e923fe2 | 227 | self.unique_id = unique_id |
Pawel Zarembski |
0:01f31e923fe2 | 228 | self.details_txt = None |
Pawel Zarembski |
0:01f31e923fe2 | 229 | self._mode = None |
Pawel Zarembski |
0:01f31e923fe2 | 230 | self._remount_count = None |
Pawel Zarembski |
0:01f31e923fe2 | 231 | self._assert = None |
Pawel Zarembski |
0:01f31e923fe2 | 232 | self._check_fs_on_remount = False |
Pawel Zarembski |
0:01f31e923fe2 | 233 | self._manage_assert = False |
Pawel Zarembski |
0:01f31e923fe2 | 234 | self.update_board_info() |
Pawel Zarembski |
0:01f31e923fe2 | 235 | |
Pawel Zarembski |
0:01f31e923fe2 | 236 | def __str__(self): |
Pawel Zarembski |
0:01f31e923fe2 | 237 | return "Name=%s Unique ID=%s" % (self.name, self.get_unique_id()) |
Pawel Zarembski |
0:01f31e923fe2 | 238 | |
Pawel Zarembski |
0:01f31e923fe2 | 239 | def get_unique_id(self): |
Pawel Zarembski |
0:01f31e923fe2 | 240 | return self.unique_id |
Pawel Zarembski |
0:01f31e923fe2 | 241 | |
Pawel Zarembski |
0:01f31e923fe2 | 242 | def get_board_id(self): |
Pawel Zarembski |
0:01f31e923fe2 | 243 | return self.board_id |
Pawel Zarembski |
0:01f31e923fe2 | 244 | |
Pawel Zarembski |
0:01f31e923fe2 | 245 | @property |
Pawel Zarembski |
0:01f31e923fe2 | 246 | def hic_id(self): |
Pawel Zarembski |
0:01f31e923fe2 | 247 | return self._hic_id |
Pawel Zarembski |
0:01f31e923fe2 | 248 | |
Pawel Zarembski |
0:01f31e923fe2 | 249 | @property |
Pawel Zarembski |
0:01f31e923fe2 | 250 | def name(self): |
Pawel Zarembski |
0:01f31e923fe2 | 251 | if self.board_id in info.BOARD_ID_TO_BUILD_TARGET: |
Pawel Zarembski |
0:01f31e923fe2 | 252 | board_target = info.BOARD_ID_TO_BUILD_TARGET[self.board_id] |
Pawel Zarembski |
0:01f31e923fe2 | 253 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 254 | board_target = "Unknown" |
Pawel Zarembski |
0:01f31e923fe2 | 255 | return board_target |
Pawel Zarembski |
0:01f31e923fe2 | 256 | |
Pawel Zarembski |
0:01f31e923fe2 | 257 | def get_serial_port(self): |
Pawel Zarembski |
0:01f31e923fe2 | 258 | return self.serial_port |
Pawel Zarembski |
0:01f31e923fe2 | 259 | |
Pawel Zarembski |
0:01f31e923fe2 | 260 | def get_mount_point(self): |
Pawel Zarembski |
0:01f31e923fe2 | 261 | return self.mount_point |
Pawel Zarembski |
0:01f31e923fe2 | 262 | |
Pawel Zarembski |
0:01f31e923fe2 | 263 | def get_connected(self): |
Pawel Zarembski |
0:01f31e923fe2 | 264 | """Check if the board is connected""" |
Pawel Zarembski |
0:01f31e923fe2 | 265 | return os.path.isdir(self.mount_point) |
Pawel Zarembski |
0:01f31e923fe2 | 266 | |
Pawel Zarembski |
0:01f31e923fe2 | 267 | def get_failure_message_and_type(self): |
Pawel Zarembski |
0:01f31e923fe2 | 268 | """Get the failure message and types from fail.txt |
Pawel Zarembski |
0:01f31e923fe2 | 269 | |
Pawel Zarembski |
0:01f31e923fe2 | 270 | return None if there there is no failure |
Pawel Zarembski |
0:01f31e923fe2 | 271 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 272 | error = None |
Pawel Zarembski |
0:01f31e923fe2 | 273 | error_type = None |
Pawel Zarembski |
0:01f31e923fe2 | 274 | fail_file = self.get_file_path('FAIL.TXT') |
Pawel Zarembski |
0:01f31e923fe2 | 275 | if not self.get_connected(): |
Pawel Zarembski |
0:01f31e923fe2 | 276 | raise Exception('Board not connected') |
Pawel Zarembski |
0:01f31e923fe2 | 277 | if os.path.isfile(fail_file): |
Pawel Zarembski |
0:01f31e923fe2 | 278 | with open(fail_file, 'r') as fail_file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 279 | msg = fail_file_handle.read() |
Pawel Zarembski |
0:01f31e923fe2 | 280 | lines = msg.splitlines() |
Pawel Zarembski |
0:01f31e923fe2 | 281 | if len(lines) == 2: |
Pawel Zarembski |
0:01f31e923fe2 | 282 | if lines[0].startswith('error: '): |
Pawel Zarembski |
0:01f31e923fe2 | 283 | error = lines[0][7:] |
Pawel Zarembski |
0:01f31e923fe2 | 284 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 285 | raise Exception('Can not parse error line in FAIL.TXT') |
Pawel Zarembski |
0:01f31e923fe2 | 286 | if lines[1].startswith('type: '): |
Pawel Zarembski |
0:01f31e923fe2 | 287 | error_type = lines[1][6:] |
Pawel Zarembski |
0:01f31e923fe2 | 288 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 289 | raise Exception('Can not parse type line in FAIL.TXT') |
Pawel Zarembski |
0:01f31e923fe2 | 290 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 291 | raise Exception('Wrong number of lines in FAIL.TXT, expected: 2') |
Pawel Zarembski |
0:01f31e923fe2 | 292 | return error, error_type |
Pawel Zarembski |
0:01f31e923fe2 | 293 | |
Pawel Zarembski |
0:01f31e923fe2 | 294 | def get_assert_info(self): |
Pawel Zarembski |
0:01f31e923fe2 | 295 | """Return an AssertInfo if an assert occurred, else None""" |
Pawel Zarembski |
0:01f31e923fe2 | 296 | return self._assert |
Pawel Zarembski |
0:01f31e923fe2 | 297 | |
Pawel Zarembski |
0:01f31e923fe2 | 298 | def get_mode(self): |
Pawel Zarembski |
0:01f31e923fe2 | 299 | """Return either MODE_IF or MODE_BL""" |
Pawel Zarembski |
0:01f31e923fe2 | 300 | assert ((self._mode is DaplinkBoard.MODE_BL) or |
Pawel Zarembski |
0:01f31e923fe2 | 301 | (self._mode is DaplinkBoard.MODE_IF)) |
Pawel Zarembski |
0:01f31e923fe2 | 302 | return self._mode |
Pawel Zarembski |
0:01f31e923fe2 | 303 | |
Pawel Zarembski |
0:01f31e923fe2 | 304 | def get_file_path(self, file_name): |
Pawel Zarembski |
0:01f31e923fe2 | 305 | """Convenience function to the path to a file on the drive""" |
Pawel Zarembski |
0:01f31e923fe2 | 306 | return os.path.normpath(self.mount_point + os.sep + file_name) |
Pawel Zarembski |
0:01f31e923fe2 | 307 | |
Pawel Zarembski |
0:01f31e923fe2 | 308 | def refresh(self, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 309 | """Remount driver to get updated contents""" |
Pawel Zarembski |
0:01f31e923fe2 | 310 | refresh_filename = self.get_file_path('REFRESH.ACT') |
Pawel Zarembski |
0:01f31e923fe2 | 311 | with open(refresh_filename, 'wb') as _: |
Pawel Zarembski |
0:01f31e923fe2 | 312 | pass |
Pawel Zarembski |
0:01f31e923fe2 | 313 | self.wait_for_remount(parent_test) |
Pawel Zarembski |
0:01f31e923fe2 | 314 | |
Pawel Zarembski |
0:01f31e923fe2 | 315 | def set_mode(self, mode, parent_test=None): |
Pawel Zarembski |
0:01f31e923fe2 | 316 | """Set the mode to either MODE_IF or MODE_BL""" |
Pawel Zarembski |
0:01f31e923fe2 | 317 | assert ((mode is DaplinkBoard.MODE_BL) or |
Pawel Zarembski |
0:01f31e923fe2 | 318 | (mode is DaplinkBoard.MODE_IF)) |
Pawel Zarembski |
0:01f31e923fe2 | 319 | if parent_test is None: |
Pawel Zarembski |
0:01f31e923fe2 | 320 | parent_test = TestInfoStub() |
Pawel Zarembski |
0:01f31e923fe2 | 321 | test_info = parent_test.create_subtest('set_mode') |
Pawel Zarembski |
0:01f31e923fe2 | 322 | current_mode = self.get_mode() |
Pawel Zarembski |
0:01f31e923fe2 | 323 | if current_mode is mode: |
Pawel Zarembski |
0:01f31e923fe2 | 324 | # No mode change needed |
Pawel Zarembski |
0:01f31e923fe2 | 325 | return |
Pawel Zarembski |
0:01f31e923fe2 | 326 | |
Pawel Zarembski |
0:01f31e923fe2 | 327 | if mode is self.MODE_BL: |
Pawel Zarembski |
0:01f31e923fe2 | 328 | test_info.info("changing mode IF -> BL") |
Pawel Zarembski |
0:01f31e923fe2 | 329 | # Create file to enter BL mode |
Pawel Zarembski |
0:01f31e923fe2 | 330 | start_bl_path = self.get_file_path('START_BL.ACT') |
Pawel Zarembski |
0:01f31e923fe2 | 331 | with open(start_bl_path, 'wb') as _: pass |
Pawel Zarembski |
0:01f31e923fe2 | 332 | elif mode is self.MODE_IF: |
Pawel Zarembski |
0:01f31e923fe2 | 333 | test_info.info("changing mode BL -> IF") |
Pawel Zarembski |
0:01f31e923fe2 | 334 | # Create file to enter IF mode |
Pawel Zarembski |
0:01f31e923fe2 | 335 | start_if_path = self.get_file_path('START_IF.ACT') |
Pawel Zarembski |
0:01f31e923fe2 | 336 | with open(start_if_path, 'wb') as _: pass |
Pawel Zarembski |
0:01f31e923fe2 | 337 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 338 | test_info.warning("Board is in unknown mode") |
Pawel Zarembski |
0:01f31e923fe2 | 339 | self.wait_for_remount(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 340 | |
Pawel Zarembski |
0:01f31e923fe2 | 341 | new_mode = self.get_mode() |
Pawel Zarembski |
0:01f31e923fe2 | 342 | if new_mode != mode: |
Pawel Zarembski |
0:01f31e923fe2 | 343 | test_info.failure("Board in wrong mode: %s" % new_mode) |
Pawel Zarembski |
0:01f31e923fe2 | 344 | raise Exception("Could not change board mode") |
Pawel Zarembski |
0:01f31e923fe2 | 345 | |
Pawel Zarembski |
0:01f31e923fe2 | 346 | def set_check_fs_on_remount(self, enabled): |
Pawel Zarembski |
0:01f31e923fe2 | 347 | assert isinstance(enabled, bool) |
Pawel Zarembski |
0:01f31e923fe2 | 348 | self._check_fs_on_remount = enabled |
Pawel Zarembski |
0:01f31e923fe2 | 349 | self.set_assert_auto_manage(enabled) |
Pawel Zarembski |
0:01f31e923fe2 | 350 | |
Pawel Zarembski |
0:01f31e923fe2 | 351 | def set_assert_auto_manage(self, enabled): |
Pawel Zarembski |
0:01f31e923fe2 | 352 | assert isinstance(enabled, bool) |
Pawel Zarembski |
0:01f31e923fe2 | 353 | self.clear_assert() |
Pawel Zarembski |
0:01f31e923fe2 | 354 | self._manage_assert = enabled |
Pawel Zarembski |
0:01f31e923fe2 | 355 | |
Pawel Zarembski |
0:01f31e923fe2 | 356 | def clear_assert(self): |
Pawel Zarembski |
0:01f31e923fe2 | 357 | assert_path = self.get_file_path("ASSERT.TXT") |
Pawel Zarembski |
0:01f31e923fe2 | 358 | if os.path.isfile(assert_path): |
Pawel Zarembski |
0:01f31e923fe2 | 359 | os.remove(assert_path) |
Pawel Zarembski |
0:01f31e923fe2 | 360 | self.wait_for_remount(TestInfoStub()) |
Pawel Zarembski |
0:01f31e923fe2 | 361 | |
Pawel Zarembski |
0:01f31e923fe2 | 362 | def run_board_test(self, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 363 | test_daplink.daplink_test(self, parent_test) |
Pawel Zarembski |
0:01f31e923fe2 | 364 | |
Pawel Zarembski |
0:01f31e923fe2 | 365 | def read_target_memory(self, addr, size, resume=True): |
Pawel Zarembski |
0:01f31e923fe2 | 366 | assert self.get_mode() == self.MODE_IF |
Pawel Zarembski |
0:01f31e923fe2 | 367 | with ConnectHelper.session_with_chosen_probe(unique_id=self.get_unique_id(), |
Pawel Zarembski |
0:01f31e923fe2 | 368 | resume_on_disconnect=resume) as session: |
Pawel Zarembski |
0:01f31e923fe2 | 369 | data = session.target.read_memory_block8(addr, size) |
Pawel Zarembski |
0:01f31e923fe2 | 370 | return bytearray(data) |
Pawel Zarembski |
0:01f31e923fe2 | 371 | |
Pawel Zarembski |
0:01f31e923fe2 | 372 | def test_fs(self, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 373 | """Check if the raw filesystem is valid""" |
Pawel Zarembski |
0:01f31e923fe2 | 374 | if sys.platform.startswith("win"): |
Pawel Zarembski |
0:01f31e923fe2 | 375 | test_info = parent_test.create_subtest('test_fs') |
Pawel Zarembski |
0:01f31e923fe2 | 376 | returncode = _run_chkdsk(self.mount_point) |
Pawel Zarembski |
0:01f31e923fe2 | 377 | test_info.info('chkdsk returned %s' % returncode) |
Pawel Zarembski |
0:01f31e923fe2 | 378 | if returncode != 0: |
Pawel Zarembski |
0:01f31e923fe2 | 379 | test_info.failure('Disk corrupt') |
Pawel Zarembski |
0:01f31e923fe2 | 380 | |
Pawel Zarembski |
0:01f31e923fe2 | 381 | # Windows 8/10 workaround - rerun chkdsk until disk caching is on |
Pawel Zarembski |
0:01f31e923fe2 | 382 | # Notes about this problem: |
Pawel Zarembski |
0:01f31e923fe2 | 383 | # - This is less likely to occur when the "storage" service is |
Pawel Zarembski |
0:01f31e923fe2 | 384 | # turned off and/or you are running as administrator |
Pawel Zarembski |
0:01f31e923fe2 | 385 | # - When creating a directory with os.mkdir the |
Pawel Zarembski |
0:01f31e923fe2 | 386 | # following error occurs: "WindowsError: [Error 1392] The |
Pawel Zarembski |
0:01f31e923fe2 | 387 | # file or directory is corrupted and unreadable: '<directory>'" |
Pawel Zarembski |
0:01f31e923fe2 | 388 | # - When creating a file with open(<filename>, "wb") the |
Pawel Zarembski |
0:01f31e923fe2 | 389 | # following error occurs: "OError: [Errno 22] invalid |
Pawel Zarembski |
0:01f31e923fe2 | 390 | # mode ('wb') or filename: '<filename>'" |
Pawel Zarembski |
0:01f31e923fe2 | 391 | # - When a file or directory is created on the drive in explorer |
Pawel Zarembski |
0:01f31e923fe2 | 392 | # and you preform a refresh, the newly created file or |
Pawel Zarembski |
0:01f31e923fe2 | 393 | # directory disappears |
Pawel Zarembski |
0:01f31e923fe2 | 394 | persist_test_dir = self.get_file_path("persist_test_dir") |
Pawel Zarembski |
0:01f31e923fe2 | 395 | for _ in range(10): |
Pawel Zarembski |
0:01f31e923fe2 | 396 | try: |
Pawel Zarembski |
0:01f31e923fe2 | 397 | os.mkdir(persist_test_dir) |
Pawel Zarembski |
0:01f31e923fe2 | 398 | except EnvironmentError as exception: |
Pawel Zarembski |
0:01f31e923fe2 | 399 | test_info.info("cache check exception %s" % exception) |
Pawel Zarembski |
0:01f31e923fe2 | 400 | if os.path.exists(persist_test_dir): |
Pawel Zarembski |
0:01f31e923fe2 | 401 | os.rmdir(persist_test_dir) |
Pawel Zarembski |
0:01f31e923fe2 | 402 | break |
Pawel Zarembski |
0:01f31e923fe2 | 403 | test_info.info("running checkdisk to re-enable caching") |
Pawel Zarembski |
0:01f31e923fe2 | 404 | _run_chkdsk(self.mount_point) |
Pawel Zarembski |
0:01f31e923fe2 | 405 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 406 | raise Exception("Unable to re-enable caching") |
Pawel Zarembski |
0:01f31e923fe2 | 407 | |
Pawel Zarembski |
0:01f31e923fe2 | 408 | # TODO - as a future improvement add linux and mac support |
Pawel Zarembski |
0:01f31e923fe2 | 409 | |
Pawel Zarembski |
0:01f31e923fe2 | 410 | # Tests for the following: |
Pawel Zarembski |
0:01f31e923fe2 | 411 | # 1. Correct files present -TODO |
Pawel Zarembski |
0:01f31e923fe2 | 412 | # 2. Contents of file are valid ascii |
Pawel Zarembski |
0:01f31e923fe2 | 413 | # 3. Each line ends with \r\n |
Pawel Zarembski |
0:01f31e923fe2 | 414 | # 4. There is no whitespace at the end of the line |
Pawel Zarembski |
0:01f31e923fe2 | 415 | # 5. Each file ends with \r\n |
Pawel Zarembski |
0:01f31e923fe2 | 416 | def test_fs_contents(self, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 417 | """Check if the file contents are valid""" |
Pawel Zarembski |
0:01f31e923fe2 | 418 | test_info = parent_test.create_subtest('test_fs_contents') |
Pawel Zarembski |
0:01f31e923fe2 | 419 | non_ascii = b'[^\x20-\x7F\r\n]' |
Pawel Zarembski |
0:01f31e923fe2 | 420 | non_cr_lf = b'\r[^\n]|[^\r]\n' |
Pawel Zarembski |
0:01f31e923fe2 | 421 | trail_white = b'(?:\ \r|\ \n)' |
Pawel Zarembski |
0:01f31e923fe2 | 422 | end_of_file = b'\r\n$' |
Pawel Zarembski |
0:01f31e923fe2 | 423 | files = os.listdir(self.mount_point) |
Pawel Zarembski |
0:01f31e923fe2 | 424 | non_ascii_re = re.compile(non_ascii) |
Pawel Zarembski |
0:01f31e923fe2 | 425 | non_cr_lf_re = re.compile(non_cr_lf) |
Pawel Zarembski |
0:01f31e923fe2 | 426 | trail_white_re = re.compile(trail_white) |
Pawel Zarembski |
0:01f31e923fe2 | 427 | end_of_file_re = re.compile(end_of_file) |
Pawel Zarembski |
0:01f31e923fe2 | 428 | for filename in files: |
Pawel Zarembski |
0:01f31e923fe2 | 429 | filepath = self.get_file_path(filename) |
Pawel Zarembski |
0:01f31e923fe2 | 430 | if not os.path.isfile(filepath): |
Pawel Zarembski |
0:01f31e923fe2 | 431 | test_info.info("Skipping non file item %s" % filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 432 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 433 | skip = False |
Pawel Zarembski |
0:01f31e923fe2 | 434 | for pattern in FILE_IGNORE_PATTERN_LIST: |
Pawel Zarembski |
0:01f31e923fe2 | 435 | if pattern.match(filename): |
Pawel Zarembski |
0:01f31e923fe2 | 436 | skip = True |
Pawel Zarembski |
0:01f31e923fe2 | 437 | break |
Pawel Zarembski |
0:01f31e923fe2 | 438 | if skip: |
Pawel Zarembski |
0:01f31e923fe2 | 439 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 440 | |
Pawel Zarembski |
0:01f31e923fe2 | 441 | with open(filepath, 'rb') as file_handle: |
Pawel Zarembski |
0:01f31e923fe2 | 442 | file_contents = file_handle.read() |
Pawel Zarembski |
0:01f31e923fe2 | 443 | if non_ascii_re.search(file_contents): |
Pawel Zarembski |
0:01f31e923fe2 | 444 | test_info.failure("Non ascii characters in %s" % filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 445 | elif non_cr_lf_re.search(file_contents): |
Pawel Zarembski |
0:01f31e923fe2 | 446 | test_info.failure("File has non-standard line endings %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 447 | filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 448 | elif trail_white_re.search(file_contents): |
Pawel Zarembski |
0:01f31e923fe2 | 449 | test_info.warning("File trailing whitespace %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 450 | filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 451 | elif end_of_file_re.search(file_contents) is None: |
Pawel Zarembski |
0:01f31e923fe2 | 452 | test_info.warning("No newline at end of file %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 453 | filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 454 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 455 | test_info.info("File %s valid" % filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 456 | |
Pawel Zarembski |
0:01f31e923fe2 | 457 | self.test_details_txt(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 458 | |
Pawel Zarembski |
0:01f31e923fe2 | 459 | def load_interface(self, filepath, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 460 | """Load an interface binary or hex""" |
Pawel Zarembski |
0:01f31e923fe2 | 461 | assert isinstance(filepath, str), "Invalid bootloader image!" |
Pawel Zarembski |
0:01f31e923fe2 | 462 | assert isinstance(parent_test, TestInfo), "Invalid parent test object!" |
Pawel Zarembski |
0:01f31e923fe2 | 463 | |
Pawel Zarembski |
0:01f31e923fe2 | 464 | test_info = parent_test.create_subtest('load_interface') |
Pawel Zarembski |
0:01f31e923fe2 | 465 | self.set_mode(self.MODE_BL, test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 466 | |
Pawel Zarembski |
0:01f31e923fe2 | 467 | data_crc, crc_in_image = _compute_crc(filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 468 | assert data_crc == crc_in_image, ("CRC in interface is wrong " |
Pawel Zarembski |
0:01f31e923fe2 | 469 | "expected 0x%x, found 0x%x" % |
Pawel Zarembski |
0:01f31e923fe2 | 470 | (data_crc, crc_in_image)) |
Pawel Zarembski |
0:01f31e923fe2 | 471 | |
Pawel Zarembski |
0:01f31e923fe2 | 472 | filename = os.path.basename(filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 473 | with open(filepath, 'rb') as firmware_file: |
Pawel Zarembski |
0:01f31e923fe2 | 474 | data = firmware_file.read() |
Pawel Zarembski |
0:01f31e923fe2 | 475 | out_file = self.get_file_path(filename) |
Pawel Zarembski |
0:01f31e923fe2 | 476 | start = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 477 | with open(out_file, 'wb') as firmware_file: |
Pawel Zarembski |
0:01f31e923fe2 | 478 | firmware_file.write(data) |
Pawel Zarembski |
0:01f31e923fe2 | 479 | stop = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 480 | test_info.info("programming took %s s" % (stop - start)) |
Pawel Zarembski |
0:01f31e923fe2 | 481 | self.wait_for_remount(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 482 | |
Pawel Zarembski |
0:01f31e923fe2 | 483 | # Check the CRC |
Pawel Zarembski |
0:01f31e923fe2 | 484 | self.set_mode(self.MODE_IF, test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 485 | if DaplinkBoard.KEY_IF_CRC not in self.details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 486 | test_info.failure("No interface CRC in details.txt") |
Pawel Zarembski |
0:01f31e923fe2 | 487 | return |
Pawel Zarembski |
0:01f31e923fe2 | 488 | details_crc = int(self.details_txt[DaplinkBoard.KEY_IF_CRC], 0) |
Pawel Zarembski |
0:01f31e923fe2 | 489 | test_info.info("Interface crc: 0x%x" % details_crc) |
Pawel Zarembski |
0:01f31e923fe2 | 490 | if data_crc != details_crc: |
Pawel Zarembski |
0:01f31e923fe2 | 491 | test_info.failure("Interface CRC is wrong") |
Pawel Zarembski |
0:01f31e923fe2 | 492 | |
Pawel Zarembski |
0:01f31e923fe2 | 493 | def load_bootloader(self, filepath, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 494 | """Load a bootloader binary or hex""" |
Pawel Zarembski |
0:01f31e923fe2 | 495 | assert isinstance(filepath, str), "Invalid bootloader image!" |
Pawel Zarembski |
0:01f31e923fe2 | 496 | assert isinstance(parent_test, TestInfo), "Invalid parent test object!" |
Pawel Zarembski |
0:01f31e923fe2 | 497 | |
Pawel Zarembski |
0:01f31e923fe2 | 498 | test_info = parent_test.create_subtest('load_bootloader') |
Pawel Zarembski |
0:01f31e923fe2 | 499 | self.set_mode(self.MODE_IF, test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 500 | |
Pawel Zarembski |
0:01f31e923fe2 | 501 | # Check image CRC |
Pawel Zarembski |
0:01f31e923fe2 | 502 | data_crc, crc_in_image = _compute_crc(filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 503 | assert data_crc == crc_in_image, ("CRC in bootloader is wrong " |
Pawel Zarembski |
0:01f31e923fe2 | 504 | "expected 0x%x, found 0x%x" % |
Pawel Zarembski |
0:01f31e923fe2 | 505 | (data_crc, crc_in_image)) |
Pawel Zarembski |
0:01f31e923fe2 | 506 | |
Pawel Zarembski |
0:01f31e923fe2 | 507 | filename = os.path.basename(filepath) |
Pawel Zarembski |
0:01f31e923fe2 | 508 | with open(filepath, 'rb') as firmware_file: |
Pawel Zarembski |
0:01f31e923fe2 | 509 | data = firmware_file.read() |
Pawel Zarembski |
0:01f31e923fe2 | 510 | out_file = self.get_file_path(filename) |
Pawel Zarembski |
0:01f31e923fe2 | 511 | start = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 512 | with open(out_file, 'wb') as firmware_file: |
Pawel Zarembski |
0:01f31e923fe2 | 513 | firmware_file.write(data) |
Pawel Zarembski |
0:01f31e923fe2 | 514 | stop = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 515 | test_info.info("programming took %s s" % (stop - start)) |
Pawel Zarembski |
0:01f31e923fe2 | 516 | self.wait_for_remount(test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 517 | |
Pawel Zarembski |
0:01f31e923fe2 | 518 | # Check the CRC |
Pawel Zarembski |
0:01f31e923fe2 | 519 | self.set_mode(self.MODE_IF, test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 520 | if DaplinkBoard.KEY_BL_CRC not in self.details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 521 | test_info.failure("No bootloader CRC in details.txt") |
Pawel Zarembski |
0:01f31e923fe2 | 522 | return |
Pawel Zarembski |
0:01f31e923fe2 | 523 | details_crc = int(self.details_txt[DaplinkBoard.KEY_BL_CRC], 0) |
Pawel Zarembski |
0:01f31e923fe2 | 524 | test_info.info("Bootloader crc: 0x%x" % details_crc) |
Pawel Zarembski |
0:01f31e923fe2 | 525 | if data_crc != details_crc: |
Pawel Zarembski |
0:01f31e923fe2 | 526 | test_info.failure("Bootloader CRC is wrong") |
Pawel Zarembski |
0:01f31e923fe2 | 527 | |
Pawel Zarembski |
0:01f31e923fe2 | 528 | def wait_for_remount(self, parent_test, wait_time=600): |
Pawel Zarembski |
0:01f31e923fe2 | 529 | mode = self._mode |
Pawel Zarembski |
0:01f31e923fe2 | 530 | count = self._remount_count |
Pawel Zarembski |
0:01f31e923fe2 | 531 | test_info = parent_test.create_subtest('wait_for_remount') |
Pawel Zarembski |
0:01f31e923fe2 | 532 | |
Pawel Zarembski |
0:01f31e923fe2 | 533 | elapsed = 0 |
Pawel Zarembski |
0:01f31e923fe2 | 534 | start = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 535 | remounted = False |
Pawel Zarembski |
0:01f31e923fe2 | 536 | while os.path.isdir(self.mount_point): |
Pawel Zarembski |
0:01f31e923fe2 | 537 | if self.update_board_info(False): #check info if it is already mounted |
Pawel Zarembski |
0:01f31e923fe2 | 538 | if mode is not None and self._mode is not None and mode is not self._mode: |
Pawel Zarembski |
0:01f31e923fe2 | 539 | remounted = True |
Pawel Zarembski |
0:01f31e923fe2 | 540 | test_info.info("already remounted with change mode") |
Pawel Zarembski |
0:01f31e923fe2 | 541 | break |
Pawel Zarembski |
0:01f31e923fe2 | 542 | elif count is not None and self._remount_count is not None and count != self._remount_count: |
Pawel Zarembski |
0:01f31e923fe2 | 543 | remounted = True |
Pawel Zarembski |
0:01f31e923fe2 | 544 | test_info.info("already remounted with change mount count") |
Pawel Zarembski |
0:01f31e923fe2 | 545 | break |
Pawel Zarembski |
0:01f31e923fe2 | 546 | if elapsed > wait_time: |
Pawel Zarembski |
0:01f31e923fe2 | 547 | raise Exception("Dismount timed out") |
Pawel Zarembski |
0:01f31e923fe2 | 548 | time.sleep(0.1) |
Pawel Zarembski |
0:01f31e923fe2 | 549 | elapsed += 0.2 |
Pawel Zarembski |
0:01f31e923fe2 | 550 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 551 | stop = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 552 | test_info.info("unmount took %s s" % (stop - start)) |
Pawel Zarembski |
0:01f31e923fe2 | 553 | elapsed = 0 |
Pawel Zarembski |
0:01f31e923fe2 | 554 | start = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 555 | |
Pawel Zarembski |
0:01f31e923fe2 | 556 | while not remounted: |
Pawel Zarembski |
0:01f31e923fe2 | 557 | if self.update_board_info(False): |
Pawel Zarembski |
0:01f31e923fe2 | 558 | if os.path.isdir(self.mount_point): |
Pawel Zarembski |
0:01f31e923fe2 | 559 | # Information returned by mbed-ls could be old. |
Pawel Zarembski |
0:01f31e923fe2 | 560 | # Only break from the loop if the second call to |
Pawel Zarembski |
0:01f31e923fe2 | 561 | # mbed-ls returns the same mount point. |
Pawel Zarembski |
0:01f31e923fe2 | 562 | tmp_mount = self.mount_point |
Pawel Zarembski |
0:01f31e923fe2 | 563 | if self.update_board_info(False): |
Pawel Zarembski |
0:01f31e923fe2 | 564 | if tmp_mount == self.mount_point: |
Pawel Zarembski |
0:01f31e923fe2 | 565 | break |
Pawel Zarembski |
0:01f31e923fe2 | 566 | if elapsed > wait_time: |
Pawel Zarembski |
0:01f31e923fe2 | 567 | raise Exception("Mount timed out") |
Pawel Zarembski |
0:01f31e923fe2 | 568 | time.sleep(0.1) |
Pawel Zarembski |
0:01f31e923fe2 | 569 | elapsed += 0.1 |
Pawel Zarembski |
0:01f31e923fe2 | 570 | stop = time.time() |
Pawel Zarembski |
0:01f31e923fe2 | 571 | test_info.info("mount took %s s" % (stop - start)) |
Pawel Zarembski |
0:01f31e923fe2 | 572 | |
Pawel Zarembski |
0:01f31e923fe2 | 573 | if count is not None and self._remount_count is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 574 | expected_count = (0 if mode is not self._mode |
Pawel Zarembski |
0:01f31e923fe2 | 575 | else (count + 1) & 0xFFFFFFFF) |
Pawel Zarembski |
0:01f31e923fe2 | 576 | if expected_count != self._remount_count: |
Pawel Zarembski |
0:01f31e923fe2 | 577 | test_info.failure('Expected remount count of %s got %s' % |
Pawel Zarembski |
0:01f31e923fe2 | 578 | (expected_count, self._remount_count)) |
Pawel Zarembski |
0:01f31e923fe2 | 579 | |
Pawel Zarembski |
0:01f31e923fe2 | 580 | # If enabled check the filesystem |
Pawel Zarembski |
0:01f31e923fe2 | 581 | if self._check_fs_on_remount: |
Pawel Zarembski |
0:01f31e923fe2 | 582 | self.test_fs(parent_test) |
Pawel Zarembski |
0:01f31e923fe2 | 583 | self.test_fs_contents(parent_test) |
Pawel Zarembski |
0:01f31e923fe2 | 584 | self.test_details_txt(parent_test) |
Pawel Zarembski |
0:01f31e923fe2 | 585 | if self._manage_assert: |
Pawel Zarembski |
0:01f31e923fe2 | 586 | if self._assert is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 587 | test_info.failure('Assert on line %s in file %s' % |
Pawel Zarembski |
0:01f31e923fe2 | 588 | (self._assert.line, self._assert.file)) |
Pawel Zarembski |
0:01f31e923fe2 | 589 | self.clear_assert() |
Pawel Zarembski |
0:01f31e923fe2 | 590 | |
Pawel Zarembski |
0:01f31e923fe2 | 591 | def update_board_info(self, exptn_on_fail=True): |
Pawel Zarembski |
0:01f31e923fe2 | 592 | """Update board info |
Pawel Zarembski |
0:01f31e923fe2 | 593 | |
Pawel Zarembski |
0:01f31e923fe2 | 594 | Update all board information variables that could |
Pawel Zarembski |
0:01f31e923fe2 | 595 | change when remounting or changing modes. |
Pawel Zarembski |
0:01f31e923fe2 | 596 | Note - before this function is set self.unique_id |
Pawel Zarembski |
0:01f31e923fe2 | 597 | must be set. |
Pawel Zarembski |
0:01f31e923fe2 | 598 | """ |
Pawel Zarembski |
0:01f31e923fe2 | 599 | |
Pawel Zarembski |
0:01f31e923fe2 | 600 | try: |
Pawel Zarembski |
0:01f31e923fe2 | 601 | endpoints = _get_board_endpoints(self.unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 602 | if endpoints is None: |
Pawel Zarembski |
0:01f31e923fe2 | 603 | if exptn_on_fail: |
Pawel Zarembski |
0:01f31e923fe2 | 604 | raise Exception("Could not update board info: %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 605 | self.unique_id) |
Pawel Zarembski |
0:01f31e923fe2 | 606 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 607 | self.unique_id, self.serial_port, self.mount_point = endpoints |
Pawel Zarembski |
0:01f31e923fe2 | 608 | # Serial port can be missing |
Pawel Zarembski |
0:01f31e923fe2 | 609 | if self.unique_id is None: |
Pawel Zarembski |
0:01f31e923fe2 | 610 | if exptn_on_fail: |
Pawel Zarembski |
0:01f31e923fe2 | 611 | raise Exception("Mount point is null") |
Pawel Zarembski |
0:01f31e923fe2 | 612 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 613 | if self.mount_point is None: |
Pawel Zarembski |
0:01f31e923fe2 | 614 | if exptn_on_fail: |
Pawel Zarembski |
0:01f31e923fe2 | 615 | raise Exception("Mount point is null") |
Pawel Zarembski |
0:01f31e923fe2 | 616 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 617 | self.board_id = int(self.unique_id[0:4], 16) |
Pawel Zarembski |
0:01f31e923fe2 | 618 | self._hic_id = int(self.unique_id[-8:], 16) |
Pawel Zarembski |
0:01f31e923fe2 | 619 | |
Pawel Zarembski |
0:01f31e923fe2 | 620 | # Note - Some legacy boards might not have details.txt |
Pawel Zarembski |
0:01f31e923fe2 | 621 | details_txt_path = self.get_file_path("details.txt") |
Pawel Zarembski |
0:01f31e923fe2 | 622 | self.details_txt = _parse_kvp_file(details_txt_path) |
Pawel Zarembski |
0:01f31e923fe2 | 623 | self._parse_assert_txt() |
Pawel Zarembski |
0:01f31e923fe2 | 624 | |
Pawel Zarembski |
0:01f31e923fe2 | 625 | self._remount_count = None |
Pawel Zarembski |
0:01f31e923fe2 | 626 | if DaplinkBoard.KEY_REMOUNT_COUNT in self.details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 627 | self._remount_count = int(self.details_txt[DaplinkBoard.KEY_REMOUNT_COUNT]) |
Pawel Zarembski |
0:01f31e923fe2 | 628 | self._mode = None |
Pawel Zarembski |
0:01f31e923fe2 | 629 | if DaplinkBoard.KEY_MODE in self.details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 630 | DETAILS_TO_MODE = { |
Pawel Zarembski |
0:01f31e923fe2 | 631 | "interface": DaplinkBoard.MODE_IF, |
Pawel Zarembski |
0:01f31e923fe2 | 632 | "bootloader": DaplinkBoard.MODE_BL, |
Pawel Zarembski |
0:01f31e923fe2 | 633 | } |
Pawel Zarembski |
0:01f31e923fe2 | 634 | mode_str = self.details_txt[DaplinkBoard.KEY_MODE] |
Pawel Zarembski |
0:01f31e923fe2 | 635 | self._mode = DETAILS_TO_MODE[mode_str] |
Pawel Zarembski |
0:01f31e923fe2 | 636 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 637 | #check for race condition here |
Pawel Zarembski |
0:01f31e923fe2 | 638 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 639 | return True |
Pawel Zarembski |
0:01f31e923fe2 | 640 | except Exception as e: |
Pawel Zarembski |
0:01f31e923fe2 | 641 | if exptn_on_fail: |
Pawel Zarembski |
0:01f31e923fe2 | 642 | raise e |
Pawel Zarembski |
0:01f31e923fe2 | 643 | else: |
Pawel Zarembski |
0:01f31e923fe2 | 644 | return False |
Pawel Zarembski |
0:01f31e923fe2 | 645 | |
Pawel Zarembski |
0:01f31e923fe2 | 646 | def test_details_txt(self, parent_test): |
Pawel Zarembski |
0:01f31e923fe2 | 647 | """Check that details.txt has all requied fields""" |
Pawel Zarembski |
0:01f31e923fe2 | 648 | test_info = parent_test.create_subtest('test_details_txt') |
Pawel Zarembski |
0:01f31e923fe2 | 649 | required_key_and_format = { |
Pawel Zarembski |
0:01f31e923fe2 | 650 | DaplinkBoard.KEY_UNIQUE_ID: re.compile("^[a-f0-9]{48}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 651 | DaplinkBoard.KEY_HIC_ID: re.compile("^[a-f0-9]{8}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 652 | DaplinkBoard.KEY_GIT_SHA: re.compile("^[a-f0-9]{40}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 653 | DaplinkBoard.KEY_LOCAL_MODS: re.compile("^[01]{1}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 654 | DaplinkBoard.KEY_USB_INTERFACES: re.compile("^.+$"), |
Pawel Zarembski |
0:01f31e923fe2 | 655 | DaplinkBoard.KEY_MODE: re.compile("(interface|bootloader)"), |
Pawel Zarembski |
0:01f31e923fe2 | 656 | } |
Pawel Zarembski |
0:01f31e923fe2 | 657 | optional_key_and_format = { |
Pawel Zarembski |
0:01f31e923fe2 | 658 | DaplinkBoard.KEY_BL_VERSION: re.compile("^[0-9]{4}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 659 | DaplinkBoard.KEY_IF_VERSION: re.compile("^[0-9]{4}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 660 | DaplinkBoard.KEY_BL_CRC: re.compile("^0x[a-f0-9]{8}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 661 | DaplinkBoard.KEY_IF_CRC: re.compile("^0x[a-f0-9]{8}$"), |
Pawel Zarembski |
0:01f31e923fe2 | 662 | } |
Pawel Zarembski |
0:01f31e923fe2 | 663 | # 1. keys and values are alphanumeric |
Pawel Zarembski |
0:01f31e923fe2 | 664 | # 2. no duplicate keys |
Pawel Zarembski |
0:01f31e923fe2 | 665 | # 3. format is key : value |
Pawel Zarembski |
0:01f31e923fe2 | 666 | # 4. required keys are present |
Pawel Zarembski |
0:01f31e923fe2 | 667 | # 5. optional keys have the expected format |
Pawel Zarembski |
0:01f31e923fe2 | 668 | details_txt_path = self.get_file_path("details.txt") |
Pawel Zarembski |
0:01f31e923fe2 | 669 | details_txt = _parse_kvp_file(details_txt_path, test_info) |
Pawel Zarembski |
0:01f31e923fe2 | 670 | if not details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 671 | test_info.failure("Could not parse details.txt") |
Pawel Zarembski |
0:01f31e923fe2 | 672 | return |
Pawel Zarembski |
0:01f31e923fe2 | 673 | |
Pawel Zarembski |
0:01f31e923fe2 | 674 | # Check for required keys |
Pawel Zarembski |
0:01f31e923fe2 | 675 | for key in required_key_and_format: |
Pawel Zarembski |
0:01f31e923fe2 | 676 | if key not in details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 677 | test_info.failure("Missing detail.txt entry: %s" % key) |
Pawel Zarembski |
0:01f31e923fe2 | 678 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 679 | |
Pawel Zarembski |
0:01f31e923fe2 | 680 | value = details_txt[key] |
Pawel Zarembski |
0:01f31e923fe2 | 681 | pattern = required_key_and_format[key] |
Pawel Zarembski |
0:01f31e923fe2 | 682 | if pattern.match(value) is None: |
Pawel Zarembski |
0:01f31e923fe2 | 683 | test_info.failure("Bad format detail.txt %s: %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 684 | (key, value)) |
Pawel Zarembski |
0:01f31e923fe2 | 685 | |
Pawel Zarembski |
0:01f31e923fe2 | 686 | # Check format of optional values |
Pawel Zarembski |
0:01f31e923fe2 | 687 | for key in optional_key_and_format: |
Pawel Zarembski |
0:01f31e923fe2 | 688 | if key not in details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 689 | continue |
Pawel Zarembski |
0:01f31e923fe2 | 690 | |
Pawel Zarembski |
0:01f31e923fe2 | 691 | value = details_txt[key] |
Pawel Zarembski |
0:01f31e923fe2 | 692 | pattern = optional_key_and_format[key] |
Pawel Zarembski |
0:01f31e923fe2 | 693 | if pattern.match(value) is None: |
Pawel Zarembski |
0:01f31e923fe2 | 694 | test_info.failure("Bad format detail.txt %s: %s" % |
Pawel Zarembski |
0:01f31e923fe2 | 695 | (key, value)) |
Pawel Zarembski |
0:01f31e923fe2 | 696 | |
Pawel Zarembski |
0:01f31e923fe2 | 697 | # Check details.txt contents |
Pawel Zarembski |
0:01f31e923fe2 | 698 | details_unique_id = None |
Pawel Zarembski |
0:01f31e923fe2 | 699 | details_hic_id = None |
Pawel Zarembski |
0:01f31e923fe2 | 700 | if DaplinkBoard.KEY_UNIQUE_ID in details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 701 | details_unique_id = details_txt[DaplinkBoard.KEY_UNIQUE_ID] |
Pawel Zarembski |
0:01f31e923fe2 | 702 | if DaplinkBoard.KEY_HIC_ID in details_txt: |
Pawel Zarembski |
0:01f31e923fe2 | 703 | details_hic_id = details_txt[DaplinkBoard.KEY_HIC_ID] |
Pawel Zarembski |
0:01f31e923fe2 | 704 | if details_unique_id is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 705 | if details_unique_id != self.unique_id: |
Pawel Zarembski |
0:01f31e923fe2 | 706 | test_info.failure("Unique ID mismatch in details.txt " |
Pawel Zarembski |
0:01f31e923fe2 | 707 | "details.txt=%s, usb=%s" % |
Pawel Zarembski |
0:01f31e923fe2 | 708 | (details_unique_id, self.unique_id)) |
Pawel Zarembski |
0:01f31e923fe2 | 709 | if details_hic_id is not None: |
Pawel Zarembski |
0:01f31e923fe2 | 710 | usb_hic = details_unique_id[-8:] |
Pawel Zarembski |
0:01f31e923fe2 | 711 | if details_hic_id != usb_hic: |
Pawel Zarembski |
0:01f31e923fe2 | 712 | test_info.failure("HIC ID is not the last 8 " |
Pawel Zarembski |
0:01f31e923fe2 | 713 | "digits of unique ID " |
Pawel Zarembski |
0:01f31e923fe2 | 714 | "details.txt=%s, usb=%s" % |
Pawel Zarembski |
0:01f31e923fe2 | 715 | (details_hic_id, usb_hic)) |
Pawel Zarembski |
0:01f31e923fe2 | 716 | |
Pawel Zarembski |
0:01f31e923fe2 | 717 | def _parse_assert_txt(self): |
Pawel Zarembski |
0:01f31e923fe2 | 718 | file_path = self.get_file_path("ASSERT.TXT") |
Pawel Zarembski |
0:01f31e923fe2 | 719 | if not os.path.isfile(file_path): |
Pawel Zarembski |
0:01f31e923fe2 | 720 | self._assert = None |
Pawel Zarembski |
0:01f31e923fe2 | 721 | return |
Pawel Zarembski |
0:01f31e923fe2 | 722 | |
Pawel Zarembski |
0:01f31e923fe2 | 723 | assert_table = _parse_kvp_file(file_path) |
Pawel Zarembski |
0:01f31e923fe2 | 724 | assert "file" in assert_table |
Pawel Zarembski |
0:01f31e923fe2 | 725 | assert "line" in assert_table |
Pawel Zarembski |
0:01f31e923fe2 | 726 | |
Pawel Zarembski |
0:01f31e923fe2 | 727 | self._assert = AssertInfo(assert_table["file"], assert_table['line']) |