1#!/usr/bin/python
2"""
3SPDX-License-Identifier: Apache-2.0
4Copyright (c) 2024 STMicroelectronics.
5This script updates ble library
6"""
7
8import sys
9import os
10import shutil
11import subprocess
12from pathlib import Path
13import logging
14from common_utils import common_utils
15
16file_list_wb = [
17    "Middlewares/ST/STM32_WPAN/ble/core/ble_bufsize.h",
18    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/hw.h",
19    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci/shci.c",
20    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/shci/shci.h",
21    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl.h",
22    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/mbox_def.h",
23    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/shci_tl_if.c",
24    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/shci_tl.c",
25    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/shci_tl.h",
26    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl.h",
27    "Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c",
28    "Middlewares/ST/STM32_WPAN/stm32_wpan_common.h",
29    "Middlewares/ST/STM32_WPAN/utilities/dbg_trace.h",
30    "Middlewares/ST/STM32_WPAN/utilities/stm_list.c",
31    "Middlewares/ST/STM32_WPAN/utilities/stm_list.h",
32    "Middlewares/ST/STM32_WPAN/utilities/utilities_common.h",
33    "Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_Hid/Core/Inc/app_common.h",
34    "Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_Hid/Core/Inc/app_conf.h",
35    "Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_Hid/Core/Inc/hw_if.h",
36    "Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_Hid/STM32_WPAN/Target/"
37    + "hw_ipcc.c",
38    "Projects/P-NUCLEO-WB55.Nucleo/Applications/BLE/BLE_Hid/STM32_WPAN/App/"
39    + "tl_dbg_conf.h",
40]
41
42ble_transparent_mode_app_path = "Projects/NUCLEO-WBA55CG/Applications/BLE/" \
43                                + "BLE_TransparentMode"
44file_list_wba = {
45    "STM32_WPAN": [
46        "Middlewares/ST/STM32_WPAN/ble/stack/include/auto/ble_types.h",
47        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/_40nm_reg_files/"
48        + "DWC_ble154combo.h",
49        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/bsp.h",
50        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/common_types.h",
51        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/event_manager.h",
52        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/evnt_schdlr_gnrc_if.h",
53        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/hci.h",
54        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/ll_intf.h",
55        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/ll_intf_cmn.h",
56        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/mem_intf.h",
57        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/os_wrapper.h",
58        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/power_table.h",
59        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/inc/pta.h",
60        "Middlewares/ST/STM32_WPAN/link_layer/ll_cmd_lib/config/ble_full/"
61        + "ll_fw_config.h",
62        "Middlewares/ST/STM32_WPAN/ble/stack/include/ble_bufsize.h",
63        "Middlewares/ST/STM32_WPAN/ble/stack/include/ble_const.h",
64        "Middlewares/ST/STM32_WPAN/ble/stack/include/ble_defs.h",
65        "Middlewares/ST/STM32_WPAN/ble/stack/include/ble_std.h",
66        "Middlewares/ST/STM32_WPAN/ble/stack/include/bleplat.h",
67        "Middlewares/ST/STM32_WPAN/ble/stack/include/blestack.h",
68        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/inc/linklayer_plat.h",
69        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/inc/ll_sys.h",
70        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/src/ll_sys_cs.c",
71        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/src/ll_sys_dp_slp.c",
72        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/src/ll_sys_intf.c",
73        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/src/ll_sys_startup.c",
74        "Middlewares/ST/STM32_WPAN/link_layer/ll_sys/inc/ll_sys_startup.h",
75        "Middlewares/ST/STM32_WPAN/stm32_wpan_common.h",
76    ],
77    "BLE_TransparentMode": [
78        ble_transparent_mode_app_path + "/Core/Inc/app_common.h",
79        ble_transparent_mode_app_path + "/Core/Inc/app_conf.h",
80        ble_transparent_mode_app_path + "/Core/Inc/app_entry.h",
81        ble_transparent_mode_app_path + "/Core/Inc/utilities_conf.h",
82        ble_transparent_mode_app_path + "/Core/Inc/main.h",
83        ble_transparent_mode_app_path + "/System/Modules/Flash/rf_timing_synchro.c",
84        ble_transparent_mode_app_path + "/System/Modules/Flash/rf_timing_synchro.h",
85        ble_transparent_mode_app_path + "/System/Modules/Flash/flash_driver.c",
86        ble_transparent_mode_app_path + "/System/Modules/Flash/flash_driver.h",
87        ble_transparent_mode_app_path + "/System/Modules/Flash/flash_manager.c",
88        ble_transparent_mode_app_path + "/System/Modules/Flash/flash_manager.h",
89        ble_transparent_mode_app_path + "/System/Modules/RTDebug/debug_signals.h",
90        ble_transparent_mode_app_path + "/System/Modules/RTDebug/RTDebug.c",
91        ble_transparent_mode_app_path + "/System/Modules/RTDebug/RTDebug.h",
92        ble_transparent_mode_app_path + "/System/Modules/RTDebug/local_debug_tables.h",
93        ble_transparent_mode_app_path + "/System/Modules/scm.c",
94        ble_transparent_mode_app_path + "/System/Modules/scm.h",
95        ble_transparent_mode_app_path + "/System/Modules/stm_list.c",
96        ble_transparent_mode_app_path + "/System/Modules/stm_list.h",
97        ble_transparent_mode_app_path + "/System/Modules/utilities_common.h",
98        ble_transparent_mode_app_path + "/System/Interfaces/hw.h",
99        ble_transparent_mode_app_path + "/System/Interfaces/hw_aes.c",
100        ble_transparent_mode_app_path + "/System/Interfaces/hw_if.h",
101        ble_transparent_mode_app_path + "/System/Interfaces/hw_pka.c",
102        ble_transparent_mode_app_path + "/System/Interfaces/hw_pka_p256.c",
103        ble_transparent_mode_app_path + "/System/Config/Log/log_module.c",
104        ble_transparent_mode_app_path + "/System/Config/Log/log_module.h",
105        ble_transparent_mode_app_path + "/System/Config/Debug_GPIO/app_debug.h",
106        ble_transparent_mode_app_path + "/System/Config/Debug_GPIO/debug_config.h",
107        ble_transparent_mode_app_path + "/STM32_WPAN/Target/power_table.c",
108        ble_transparent_mode_app_path + "/STM32_WPAN/Target/bpka.c",
109        ble_transparent_mode_app_path + "/STM32_WPAN/Target/bpka.h",
110        ble_transparent_mode_app_path + "/STM32_WPAN/Target/linklayer_plat.c",
111        ble_transparent_mode_app_path + "/STM32_WPAN/Target/ll_sys_if.c",
112    ],
113    "Utilities": [
114        "Utilities/trace/adv_trace/stm32_adv_trace.h",
115        "Utilities/misc/stm32_mem.h",
116        "Utilities/tim_serv/stm32_timer.h",
117        "Utilities/misc/stm32_tiny_vsnprintf.h",
118    ],
119}
120
121
122def os_cmd(cmd, cwd=None, shell=False):
123    """Execute a command with subprocess.check_call()
124    Args:
125        cmd: string command to execute.
126        cwd: directory where to run command
127        shell: boolean to enable command interpretation by the shell
128
129    Returns:
130        return the returncode of the command after execution.
131    """
132    logging.debug("%s", f"{str(cmd)}      cwd:{str(cwd)}")
133
134    return subprocess.check_call(
135        cmd,
136        shell=shell,
137        cwd=cwd,
138        stdout=subprocess.DEVNULL,
139        stderr=subprocess.DEVNULL,
140    )
141
142
143def copy_ble_lib_files(src_repo_path, dest_lib_path, stm32_serie):
144    """Copy BLE lib related files from Cube Firmware to hal_stm32"""
145    if stm32_serie == "stm32wb":
146        # Remove existing *.c and *.h files
147        hci_path = Path(dest_lib_path / "hci")
148        if hci_path.exists():
149            for item in os.listdir(hci_path):
150                if item.endswith(".c") or item.endswith(".h"):
151                    os.remove(hci_path / item)
152
153        # Create hci folder if does not exist
154        if not hci_path.exists():
155            hci_path.mkdir(parents=True)
156
157        # Copy files
158        for file in file_list_wb:
159            src_file_path = Path(src_repo_path / file)
160            file_name = src_file_path.name
161            if src_file_path.exists():
162                # copy each file to destination
163                shutil.copy(src_file_path, Path(hci_path / file_name))
164            else:
165                logging.error(f"File : {src_file_path} not found")
166                logging.error("Abort")
167                sys.exit()
168
169    elif stm32_serie == "stm32wba":
170        # Remove existing *.c and *.h files
171        for root, _, files in os.walk(dest_lib_path):
172            for file in files:
173                if file.endswith(".c") or file.endswith(".h"):
174                    os.remove(os.path.join(root, file))
175
176        for dir_name in file_list_wba:
177            for file in file_list_wba[dir_name]:
178                # Src file path to be copied
179                src_file_path = Path(src_repo_path / file)
180                if src_file_path.exists():
181                    # Extract the relevant part of the path from "dir_name" onwards
182                    start_index = file.find(dir_name)
183                    relative_path = file[start_index:]
184                    # Create the full destination path
185                    destination_path = os.path.join(dest_lib_path, relative_path)
186                    # Create (if does not exist) all directories in the destination path
187                    os.makedirs(os.path.dirname(destination_path), exist_ok=True)
188                    # Copy the file to the destination path
189                    shutil.copy(src_file_path, destination_path)
190                else:
191                    # Raise all the errors but not aborting to allow a manual check
192                    logging.error(f"File : {src_file_path} not found")
193
194
195def update_ble_lib_readme(lib_path, make_version, make_commit):
196    """Update README.rst file
197
198    Args:
199        dest_lib_path: library path
200        make_version: latest STM32Cube version.
201        make_commit: Commit corresponding to latest STM32Cube version.
202    """
203
204    readme_path = Path(lib_path / "README.rst")
205
206    with readme_path.open(mode="r") as readme_prev:
207        lines = (x for x in readme_prev.read().splitlines())
208
209    readme_path.unlink()
210
211    # Write README.rst from previous one if exists
212    with open(str(readme_path), "w") as readme_file:
213        for line_item in lines:
214            # change version nb
215            if "status" in line_item.lower():
216                readme_file.write("Status:\n")
217                readme_file.write(f"   version {make_version}\n")
218                next(lines)  # skip next line
219            elif "commit" in line_item.lower():
220                readme_file.write("Commit:\n")
221                readme_file.write(f"   {make_commit}")
222                next(lines)  # skip next line
223            # change patch list with a link to the release_note.html
224            elif "Patch List" in line_item:
225                readme_file.write("Patch List:\n")
226                readme_file.write(
227                    "--> please check that the following list "
228                    + "is still valid:\n"
229                )
230            else:
231                readme_file.write(f"{line_item}\n")
232
233        readme_file.flush()
234
235
236def build_patch_from_current_zephyr_version(
237    src_repo_path, temp_source_path, zephyr_lib_path, version, stm32_serie
238):
239    """ Rebuild zephyr patch compare to cube files (current zephyr version) """
240
241    temp_source_lib_path = Path(temp_source_path / "lib" / stm32_serie)
242
243    # Checkout the current Zephyr version of the STM32Cube repo
244    os_cmd(
245        ("git", "checkout", "-f", "--recurse-submodules", version),
246        cwd=src_repo_path,
247    )
248
249    # create Cube reference from zephyr version
250    shutil.rmtree(temp_source_path, onerror=common_utils.remove_readonly)
251    temp_source_lib_path.mkdir(parents=True)
252
253    copy_ble_lib_files(
254        src_repo_path,
255        temp_source_lib_path,
256        stm32_serie,
257    )
258    os_cmd(("git", "init"), cwd=temp_source_path)
259    os_cmd(
260        ("git", "commit", "--allow-empty", "-m", "'Trigger notification'"),
261        cwd=temp_source_path,
262    )
263    os_cmd(
264        ("git", "add", "-A", Path(temp_source_lib_path / "*")),
265        cwd=temp_source_path,
266    )
267    os_cmd(
268        ("git", "commit", "-am", "ble lib from zephyr version"),
269        cwd=temp_source_path,
270    )
271
272    # Remove trailing whitespaces
273    os_cmd(
274        ("git", "rebase", "--whitespace=fix", "HEAD~1"),
275        cwd=temp_source_path,
276    )
277
278    # copy zephyr files
279    shutil.rmtree(temp_source_lib_path, onerror=common_utils.remove_readonly)
280    shutil.copytree(zephyr_lib_path, temp_source_lib_path)
281
282    # remove all files at root dir (like readme and cmakelist)
283    # so that it is not part of the patch
284    for file in temp_source_lib_path.glob("*"):
285        if file.is_file():
286            file.unlink()
287    if Path(temp_source_lib_path / "README.rst").exists():
288        Path(temp_source_lib_path / "README.rst").unlink()
289
290    # commit this current zephyr library file
291    os_cmd(("git", "add", "*"), cwd=temp_source_path)
292    os_cmd(("git", "commit", "-am", '"module"'), cwd=temp_source_path)
293
294    # Remove trailing space
295    os_cmd(
296        ("git", "rebase", "--whitespace=fix", "HEAD~1"),
297        cwd=temp_source_path,
298    )
299
300    # For unclear reason, using tuple ("git", "diff", ...) is failing on Linux
301    # especially for this command. Keep a single string.
302    os_cmd(
303        (
304            "git diff --ignore-space-at-eol HEAD~1 --output="
305            + str(zephyr_lib_path)
306            + "/ble_zephyr.patch"
307        ),
308        shell=True,
309        cwd=temp_source_path,
310    )
311    os_cmd(("dos2unix", "ble_zephyr.patch"), cwd=zephyr_lib_path)
312
313
314def update(
315    src_repo_path,
316    dest_lib_path,
317    temp_source_path,
318    current_version,
319    update_version,
320    commit,
321    stm32_serie
322):
323    """Update ble library"""
324    logging.info(" ... Updating ble library ...")
325    if not dest_lib_path.exists():
326        dest_lib_path.mkdir(parents=True)
327
328    build_patch_from_current_zephyr_version(
329        src_repo_path, temp_source_path, dest_lib_path, current_version,
330        stm32_serie
331    )
332    # Checkout the latest version of the STM32Cube repo
333    os_cmd(
334        ("git", "checkout", "-f", "--recurse-submodules", update_version),
335        cwd=src_repo_path,
336    )
337    copy_ble_lib_files(src_repo_path, dest_lib_path, stm32_serie)
338    common_utils.apply_patch(dest_lib_path / "ble_zephyr.patch", dest_lib_path)
339    if Path(dest_lib_path / "README.rst").exists():
340        update_ble_lib_readme(dest_lib_path, update_version, commit)
341
342
343if __name__ == "__main__":
344    print("This script is not intend to be called directly\n")
345    print("It is used through serie_update.py\n")
346    sys.exit()
347