mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 21:08:19 +00:00
refactor zephyr patches to use proper git apply
This commit is contained in:
+112
-40
@@ -4,56 +4,128 @@
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
# ============================================================================
|
||||
# Zephyr Patch Auto-Apply
|
||||
# Zephyr Patch Auto-Apply (unified diffs via git apply)
|
||||
# ============================================================================
|
||||
# Patches stored in zephcore/patches/zephyr/ are copied to Zephyr source tree
|
||||
# at configure time. This survives `west update` - patches are reapplied on
|
||||
# next build.
|
||||
# Patches are applied at configure time using `git apply`. This means:
|
||||
# - If upstream changed in a conflicting area → build FAILS with clear error
|
||||
# - If upstream changed in non-conflicting areas → changes are preserved
|
||||
# - Idempotent: re-running cmake without west update detects already-applied patches
|
||||
#
|
||||
# Current patches:
|
||||
# zephyr/:
|
||||
# - drivers/lora/loramac-node/sx12xx_common.c: bandwidth enum→index mapping
|
||||
# - drivers/lora/lr11xx/*: LR11xx Zephyr LoRa driver (for future upstream PR)
|
||||
# - drivers/lora/CMakeLists.txt: adds lr11xx subdirectory
|
||||
# - drivers/lora/Kconfig: adds lr11xx Kconfig
|
||||
# - dts/bindings/lora/semtech,lr1110.yaml: LR1110 DTS binding
|
||||
# - drivers/gnss/gnss_luatos_air530z.c: EASY ephemeris prediction (PMTK869)
|
||||
# - drivers/gnss/Kconfig.luatos_air530z: CONFIG_GNSS_LUATOS_AIR530Z_EASY
|
||||
# - drivers/lora/native/sx126x/sx126x.c: fix rx-enable-gpios not toggled
|
||||
# when dio2-tx-enable is set (breaks E22-900M30S and similar PA modules)
|
||||
# modules/:
|
||||
# - lib/loramac-node/src/radio/sx126x/radio.c: restore full 10-element
|
||||
# Bandwidths[] array + fix LDRO for sub-125kHz bandwidths
|
||||
# Directory layout:
|
||||
# patches/zephyr/*.patch - unified diffs applied to the Zephyr tree
|
||||
# patches/zephyr-new/ - new files copied to the Zephyr tree (no upstream)
|
||||
# patches/loramac-node/*.patch - unified diffs for modules/lib/loramac-node
|
||||
# patches/espressif/*.patch - unified diffs for modules/hal/espressif
|
||||
#
|
||||
# Zephyr patches:
|
||||
# 0001-lora-lr11xx-build - CMakeLists.txt + Kconfig (lr11xx subdirectory)
|
||||
# 0002-lora-sx12xx-common - RX error callback, bandwidth mapping, fast TX/RX
|
||||
# 0003-lora-sx126x-native - PA module RF switch fix (dio2-tx-enable)
|
||||
# 0004-lora-sx126x-standalone - DIO1 IRQ error logging
|
||||
# 0005-gnss-air530z-easy - EASY ephemeris prediction (PMTK869) + Kconfig
|
||||
# 0006-blobs-py - west blobs command fix
|
||||
# New files (copied, not patched):
|
||||
# drivers/lora/lr11xx/* - LR11xx Zephyr LoRa driver
|
||||
# dts/bindings/lora/semtech,lr1110.yaml - LR1110 DTS binding
|
||||
# Module patches:
|
||||
# loramac-node: restore 10-element Bandwidths[] + LDRO fix
|
||||
# espressif: MCUboot config flexibility (mbedtls, validation, sector count)
|
||||
#
|
||||
|
||||
# --- Helper: apply unified diff patches via git apply ---
|
||||
function(zephcore_apply_patches PATCH_DIR TARGET_DIR LABEL)
|
||||
file(GLOB PATCH_FILES "${PATCH_DIR}/*.patch")
|
||||
list(SORT PATCH_FILES)
|
||||
foreach(PATCH_FILE ${PATCH_FILES})
|
||||
get_filename_component(PATCH_NAME ${PATCH_FILE} NAME)
|
||||
# Check if already applied (idempotent for re-configure without west update)
|
||||
execute_process(
|
||||
COMMAND git apply --reverse --check "${PATCH_FILE}"
|
||||
WORKING_DIRECTORY "${TARGET_DIR}"
|
||||
RESULT_VARIABLE ALREADY_APPLIED
|
||||
OUTPUT_QUIET ERROR_QUIET
|
||||
)
|
||||
if(ALREADY_APPLIED EQUAL 0)
|
||||
message(STATUS " [${LABEL}] Already applied: ${PATCH_NAME}")
|
||||
continue()
|
||||
endif()
|
||||
# Verify patch applies cleanly
|
||||
execute_process(
|
||||
COMMAND git apply --check "${PATCH_FILE}"
|
||||
WORKING_DIRECTORY "${TARGET_DIR}"
|
||||
RESULT_VARIABLE PATCH_CHECK
|
||||
ERROR_VARIABLE PATCH_ERR
|
||||
)
|
||||
if(NOT PATCH_CHECK EQUAL 0)
|
||||
message(FATAL_ERROR
|
||||
"ZephCore patch FAILED to apply: ${PATCH_NAME}\n"
|
||||
"Target tree: ${TARGET_DIR}\n"
|
||||
"Error:\n${PATCH_ERR}\n"
|
||||
"Upstream likely changed — rebase the patch:\n"
|
||||
" cd ${TARGET_DIR}\n"
|
||||
" git diff -- <file> # inspect current upstream\n"
|
||||
" # Regenerate: git diff -- <file> > ${PATCH_FILE}\n"
|
||||
)
|
||||
endif()
|
||||
# Apply the patch
|
||||
execute_process(
|
||||
COMMAND git apply "${PATCH_FILE}"
|
||||
WORKING_DIRECTORY "${TARGET_DIR}"
|
||||
RESULT_VARIABLE APPLY_RESULT
|
||||
ERROR_VARIABLE APPLY_ERR
|
||||
)
|
||||
if(NOT APPLY_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "git apply failed unexpectedly: ${PATCH_NAME}\n${APPLY_ERR}")
|
||||
endif()
|
||||
message(STATUS " [${LABEL}] Applied: ${PATCH_NAME}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Resolve target directories (before find_package(Zephyr) sets ZEPHYR_BASE)
|
||||
get_filename_component(ZEPHYR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE)
|
||||
get_filename_component(MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../modules ABSOLUTE)
|
||||
|
||||
# Apply unified diff patches to Zephyr tree
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr)
|
||||
message(STATUS "Applying ZephCore patches to Zephyr...")
|
||||
file(GLOB_RECURSE ZEPHCORE_PATCH_FILES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr/*)
|
||||
foreach(REL_PATH ${ZEPHCORE_PATCH_FILES})
|
||||
set(SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr/${REL_PATH})
|
||||
# ZEPHYR_BASE is set by find_package(Zephyr), but we're before that.
|
||||
# Use the parent directory structure: zephcore/../zephyr
|
||||
get_filename_component(ZEPHYR_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../zephyr ABSOLUTE)
|
||||
zephcore_apply_patches(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr"
|
||||
"${ZEPHYR_DIR}"
|
||||
"zephyr"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Copy new files (no upstream equivalent) to Zephyr tree
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new)
|
||||
file(GLOB_RECURSE ZEPHCORE_NEW_FILES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new/*)
|
||||
foreach(REL_PATH ${ZEPHCORE_NEW_FILES})
|
||||
set(SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/patches/zephyr-new/${REL_PATH})
|
||||
set(DST_FILE ${ZEPHYR_DIR}/${REL_PATH})
|
||||
message(STATUS " Patching: ${REL_PATH}")
|
||||
configure_file(${SRC_FILE} ${DST_FILE} COPYONLY)
|
||||
message(STATUS " [zephyr-new] ${REL_PATH}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Patch modules (loramac-node, etc.) - same mechanism, different target dir
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/modules)
|
||||
message(STATUS "Applying ZephCore patches to modules...")
|
||||
file(GLOB_RECURSE ZEPHCORE_MODULE_PATCH_FILES
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/patches/modules
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/patches/modules/*)
|
||||
foreach(REL_PATH ${ZEPHCORE_MODULE_PATCH_FILES})
|
||||
set(SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/patches/modules/${REL_PATH})
|
||||
get_filename_component(MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../modules ABSOLUTE)
|
||||
set(DST_FILE ${MODULES_DIR}/${REL_PATH})
|
||||
message(STATUS " Patching: ${REL_PATH}")
|
||||
configure_file(${SRC_FILE} ${DST_FILE} COPYONLY)
|
||||
endforeach()
|
||||
# Apply unified diff patches to loramac-node module
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/loramac-node)
|
||||
message(STATUS "Applying ZephCore patches to loramac-node...")
|
||||
zephcore_apply_patches(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/patches/loramac-node"
|
||||
"${MODULES_DIR}/lib/loramac-node"
|
||||
"loramac-node"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Apply unified diff patches to espressif module
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/patches/espressif)
|
||||
message(STATUS "Applying ZephCore patches to espressif...")
|
||||
zephcore_apply_patches(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/patches/espressif"
|
||||
"${MODULES_DIR}/hal/espressif"
|
||||
"espressif"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add custom boards directory (for Wio Tracker L1, etc.)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
diff --git a/zephyr/port/include/boot/mcuboot_config/mcuboot_config.h b/zephyr/port/include/boot/mcuboot_config/mcuboot_config.h
|
||||
index 472ebbc5ab..9cec9f7e12 100644
|
||||
--- a/zephyr/port/include/boot/mcuboot_config/mcuboot_config.h
|
||||
+++ b/zephyr/port/include/boot/mcuboot_config/mcuboot_config.h
|
||||
@@ -2,6 +2,10 @@
|
||||
* Copyright (c) 2023 Espressif Systems (Shanghai) Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
+ *
|
||||
+ * ZephCore patch: Added #ifndef guards for MCUBOOT_MAX_IMG_SECTORS,
|
||||
+ * fixed mbedTLS detection (also check CONFIG_BOOT_USE_MBEDTLS), and
|
||||
+ * made MCUBOOT_VALIDATE_PRIMARY_SLOT conditional on Kconfig.
|
||||
*/
|
||||
|
||||
#ifndef __MCUBOOT_CONFIG_H__
|
||||
@@ -76,8 +80,8 @@
|
||||
* available.
|
||||
*/
|
||||
|
||||
-/* Uncomment to use Mbed TLS cryptographic primitives */
|
||||
-#if defined(CONFIG_ESP_USE_MBEDTLS)
|
||||
+/* ZephCore patch: also check CONFIG_BOOT_USE_MBEDTLS (Zephyr Kconfig) */
|
||||
+#if defined(CONFIG_ESP_USE_MBEDTLS) || defined(CONFIG_BOOT_USE_MBEDTLS)
|
||||
#define MCUBOOT_USE_MBED_TLS
|
||||
#else
|
||||
/* MCUboot requires the definition of a crypto lib,
|
||||
@@ -90,7 +94,11 @@
|
||||
* even if no upgrade was performed. This is recommended if the boot
|
||||
* time penalty is acceptable.
|
||||
*/
|
||||
+/* ZephCore patch: honor Kconfig CONFIG_BOOT_VALIDATE_SLOT0 instead of
|
||||
+ * always validating. When unset, skip per-boot validation for faster boot. */
|
||||
+#if !defined(CONFIG_BOOT_VALIDATE_SLOT0) || CONFIG_BOOT_VALIDATE_SLOT0
|
||||
#define MCUBOOT_VALIDATE_PRIMARY_SLOT
|
||||
+#endif
|
||||
|
||||
#ifdef CONFIG_ESP_DOWNGRADE_PREVENTION
|
||||
#define MCUBOOT_DOWNGRADE_PREVENTION 1
|
||||
@@ -114,7 +122,12 @@
|
||||
|
||||
/* Default maximum number of flash sectors per image slot; change
|
||||
* as desirable. */
|
||||
+/* ZephCore patch: allow override via -D or MIN_SECTOR_COUNT from AUTO mode */
|
||||
+#if defined(MIN_SECTOR_COUNT)
|
||||
+#define MCUBOOT_MAX_IMG_SECTORS MIN_SECTOR_COUNT
|
||||
+#elif !defined(MCUBOOT_MAX_IMG_SECTORS)
|
||||
#define MCUBOOT_MAX_IMG_SECTORS 512
|
||||
+#endif
|
||||
|
||||
/* Default number of separately updateable images; change in case of
|
||||
* multiple images. */
|
||||
@@ -0,0 +1,75 @@
|
||||
diff --git a/src/radio/sx126x/radio.c b/src/radio/sx126x/radio.c
|
||||
index c4a40dab..bf6b4c9b 100644
|
||||
--- a/src/radio/sx126x/radio.c
|
||||
+++ b/src/radio/sx126x/radio.c
|
||||
@@ -421,7 +421,26 @@ const FskBandwidth_t FskBandwidths[] =
|
||||
{ 500000, 0x00 }, // Invalid Bandwidth
|
||||
};
|
||||
|
||||
-const RadioLoRaBandwidths_t Bandwidths[] = { LORA_BW_125, LORA_BW_250, LORA_BW_500 };
|
||||
+/*
|
||||
+ * ZephCore patch: restore full 10-element Bandwidths array.
|
||||
+ * Upstream Zephyr reduced this to {125, 250, 500} but the Zephyr LoRa API
|
||||
+ * now defines enums for all bandwidths (BW_7_KHZ through BW_500_KHZ).
|
||||
+ * sx12xx_common.c maps these enums to indices into this array, so all 10
|
||||
+ * entries must be present to avoid out-of-bounds access.
|
||||
+ */
|
||||
+const RadioLoRaBandwidths_t Bandwidths[] =
|
||||
+{
|
||||
+ LORA_BW_007,
|
||||
+ LORA_BW_010,
|
||||
+ LORA_BW_015,
|
||||
+ LORA_BW_020,
|
||||
+ LORA_BW_031,
|
||||
+ LORA_BW_041,
|
||||
+ LORA_BW_062,
|
||||
+ LORA_BW_125,
|
||||
+ LORA_BW_250,
|
||||
+ LORA_BW_500,
|
||||
+};
|
||||
|
||||
uint8_t MaxPayloadLength = 0xFF;
|
||||
|
||||
@@ -702,8 +721,14 @@ void RadioSetRxConfig( RadioModems_t modem, uint32_t bandwidth,
|
||||
SX126x.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth];
|
||||
SX126x.ModulationParams.Params.LoRa.CodingRate = ( RadioLoRaCodingRates_t )coderate;
|
||||
|
||||
- if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
- ( ( bandwidth == 1 ) && ( datarate == 12 ) ) )
|
||||
+ /*
|
||||
+ * ZephCore patch: LDRO check updated for 10-element Bandwidths[].
|
||||
+ * Enable LDRO when symbol duration > 16ms.
|
||||
+ * Old indices (0=125k,1=250k) → new (7=125k,8=250k,6=62.5k, etc.)
|
||||
+ */
|
||||
+ if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
+ ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ||
|
||||
+ ( ( bandwidth <= 6 ) && ( datarate >= 10 ) ) )
|
||||
{
|
||||
SX126x.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01;
|
||||
}
|
||||
@@ -809,8 +834,10 @@ void RadioSetTxConfig( RadioModems_t modem, int8_t power, uint32_t fdev,
|
||||
SX126x.ModulationParams.Params.LoRa.Bandwidth = Bandwidths[bandwidth];
|
||||
SX126x.ModulationParams.Params.LoRa.CodingRate= ( RadioLoRaCodingRates_t )coderate;
|
||||
|
||||
- if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
- ( ( bandwidth == 1 ) && ( datarate == 12 ) ) )
|
||||
+ /* ZephCore patch: LDRO check updated for 10-element Bandwidths[] */
|
||||
+ if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
+ ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ||
|
||||
+ ( ( bandwidth <= 6 ) && ( datarate >= 10 ) ) )
|
||||
{
|
||||
SX126x.ModulationParams.Params.LoRa.LowDatarateOptimize = 0x01;
|
||||
}
|
||||
@@ -946,8 +973,10 @@ static uint32_t RadioGetLoRaTimeOnAirNumerator( uint32_t bandwidth,
|
||||
}
|
||||
}
|
||||
|
||||
- if( ( ( bandwidth == 0 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
- ( ( bandwidth == 1 ) && ( datarate == 12 ) ) )
|
||||
+ /* ZephCore patch: LDRO check updated for 10-element Bandwidths[] */
|
||||
+ if( ( ( bandwidth == 7 ) && ( ( datarate == 11 ) || ( datarate == 12 ) ) ) ||
|
||||
+ ( ( bandwidth == 8 ) && ( datarate == 12 ) ) ||
|
||||
+ ( ( bandwidth <= 6 ) && ( datarate >= 10 ) ) )
|
||||
{
|
||||
lowDatareOptimize = true;
|
||||
}
|
||||
-199
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Espressif Systems (Shanghai) Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* ZephCore patch: Added #ifndef guards for MCUBOOT_MAX_IMG_SECTORS,
|
||||
* fixed mbedTLS detection (also check CONFIG_BOOT_USE_MBEDTLS), and
|
||||
* made MCUBOOT_VALIDATE_PRIMARY_SLOT conditional on Kconfig.
|
||||
*/
|
||||
|
||||
#ifndef __MCUBOOT_CONFIG_H__
|
||||
#define __MCUBOOT_CONFIG_H__
|
||||
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <watchdog.h>
|
||||
|
||||
/*
|
||||
* Signature types
|
||||
*
|
||||
* You must choose exactly one signature type - check bootloader.conf
|
||||
* configuration file
|
||||
*/
|
||||
|
||||
/* Uncomment for RSA signature support */
|
||||
#if defined(CONFIG_ESP_SIGN_RSA)
|
||||
#define MCUBOOT_SIGN_RSA
|
||||
# if (CONFIG_ESP_SIGN_RSA_LEN != 2048 && \
|
||||
CONFIG_ESP_SIGN_RSA_LEN != 3072)
|
||||
# error "Invalid RSA key size (must be 2048 or 3072)"
|
||||
# else
|
||||
# define MCUBOOT_SIGN_RSA_LEN CONFIG_ESP_SIGN_RSA_LEN
|
||||
# endif
|
||||
#elif defined(CONFIG_ESP_SIGN_EC256)
|
||||
#define MCUBOOT_SIGN_EC256
|
||||
#elif defined(CONFIG_ESP_SIGN_ED25519)
|
||||
#define MCUBOOT_SIGN_ED25519
|
||||
#endif
|
||||
|
||||
/* This mcuboot_config.h is used only by Zephyr builds, such as MCUboot
|
||||
* Zephyr Port for ESP chips or a Zephyr application with MCUboot
|
||||
* compatibilty (that builds the bootutil lib).
|
||||
* In these cases, MCUBOOT_BOOT_MAX_ALIGN value must be taken from DT if
|
||||
* the write-block-size is greater than 8 */
|
||||
#if DT_PROP(DT_CHOSEN(zephyr_flash), write_block_size) > 8
|
||||
#define MCUBOOT_BOOT_MAX_ALIGN DT_PROP(DT_CHOSEN(zephyr_flash), write_block_size)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Upgrade mode
|
||||
*
|
||||
* The default is to support A/B image swapping with rollback. Other modes
|
||||
* with simpler code path, which only supports overwriting the existing image
|
||||
* with the update image or running the newest image directly from its flash
|
||||
* partition, are also available.
|
||||
*
|
||||
* You can enable only one mode at a time from the list below to override
|
||||
* the default upgrade mode.
|
||||
*/
|
||||
|
||||
/* Uncomment to enable the overwrite-only code path. */
|
||||
/* #define MCUBOOT_OVERWRITE_ONLY */
|
||||
|
||||
#ifdef MCUBOOT_OVERWRITE_ONLY
|
||||
/* Uncomment to only erase and overwrite those primary slot sectors needed
|
||||
* to install the new image, rather than the entire image slot. */
|
||||
/* #define MCUBOOT_OVERWRITE_ONLY_FAST */
|
||||
#endif
|
||||
|
||||
/* Uncomment to enable the direct-xip code path. */
|
||||
/* #define MCUBOOT_DIRECT_XIP */
|
||||
|
||||
/* Uncomment to enable the ram-load code path. */
|
||||
/* #define MCUBOOT_RAM_LOAD */
|
||||
|
||||
/*
|
||||
* Cryptographic settings
|
||||
*
|
||||
* You must choose between Mbed TLS and Tinycrypt as source of
|
||||
* cryptographic primitives. Other cryptographic settings are also
|
||||
* available.
|
||||
*/
|
||||
|
||||
/* ZephCore patch: also check CONFIG_BOOT_USE_MBEDTLS (Zephyr Kconfig) */
|
||||
#if defined(CONFIG_ESP_USE_MBEDTLS) || defined(CONFIG_BOOT_USE_MBEDTLS)
|
||||
#define MCUBOOT_USE_MBED_TLS
|
||||
#else
|
||||
/* MCUboot requires the definition of a crypto lib,
|
||||
* using Tinycrypt as default */
|
||||
#define MCUBOOT_USE_TINYCRYPT
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Always check the signature of the image in the primary slot before booting,
|
||||
* even if no upgrade was performed. This is recommended if the boot
|
||||
* time penalty is acceptable.
|
||||
*/
|
||||
/* ZephCore patch: honor Kconfig CONFIG_BOOT_VALIDATE_SLOT0 instead of
|
||||
* always validating. When unset, skip per-boot validation for faster boot. */
|
||||
#if !defined(CONFIG_BOOT_VALIDATE_SLOT0) || CONFIG_BOOT_VALIDATE_SLOT0
|
||||
#define MCUBOOT_VALIDATE_PRIMARY_SLOT
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_DOWNGRADE_PREVENTION
|
||||
#define MCUBOOT_DOWNGRADE_PREVENTION 1
|
||||
/* MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER is used later as bool value so it is
|
||||
* always defined, (unlike MCUBOOT_DOWNGRADE_PREVENTION which is only used in
|
||||
* preprocessor condition and my be not defined) */
|
||||
# ifdef CONFIG_ESP_DOWNGRADE_PREVENTION_SECURITY_COUNTER
|
||||
# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 1
|
||||
# else
|
||||
# define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Flash abstraction
|
||||
*/
|
||||
|
||||
/* Uncomment if your flash map API supports flash_area_get_sectors().
|
||||
* See the flash APIs for more details. */
|
||||
#define MCUBOOT_USE_FLASH_AREA_GET_SECTORS
|
||||
|
||||
/* Default maximum number of flash sectors per image slot; change
|
||||
* as desirable. */
|
||||
/* ZephCore patch: allow override via -D or MIN_SECTOR_COUNT from AUTO mode */
|
||||
#if defined(MIN_SECTOR_COUNT)
|
||||
#define MCUBOOT_MAX_IMG_SECTORS MIN_SECTOR_COUNT
|
||||
#elif !defined(MCUBOOT_MAX_IMG_SECTORS)
|
||||
#define MCUBOOT_MAX_IMG_SECTORS 512
|
||||
#endif
|
||||
|
||||
/* Default number of separately updateable images; change in case of
|
||||
* multiple images. */
|
||||
#if defined(CONFIG_ESP_IMAGE_NUMBER)
|
||||
#define MCUBOOT_IMAGE_NUMBER CONFIG_ESP_IMAGE_NUMBER
|
||||
#else
|
||||
#define MCUBOOT_IMAGE_NUMBER 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Logging
|
||||
*/
|
||||
|
||||
/*
|
||||
* If logging is enabled the following functions must be defined by the
|
||||
* platform:
|
||||
*
|
||||
* MCUBOOT_LOG_MODULE_REGISTER(domain)
|
||||
* Register a new log module and add the current C file to it.
|
||||
*
|
||||
* MCUBOOT_LOG_MODULE_DECLARE(domain)
|
||||
* Add the current C file to an existing log module.
|
||||
*
|
||||
* MCUBOOT_LOG_ERR(...)
|
||||
* MCUBOOT_LOG_WRN(...)
|
||||
* MCUBOOT_LOG_INF(...)
|
||||
* MCUBOOT_LOG_DBG(...)
|
||||
*
|
||||
* The function priority is:
|
||||
*
|
||||
* MCUBOOT_LOG_ERR > MCUBOOT_LOG_WRN > MCUBOOT_LOG_INF > MCUBOOT_LOG_DBG
|
||||
*/
|
||||
#define MCUBOOT_HAVE_LOGGING 1
|
||||
/* #define MCUBOOT_LOG_LEVEL MCUBOOT_LOG_LEVEL_INFO */
|
||||
|
||||
/*
|
||||
* Assertions
|
||||
*/
|
||||
|
||||
/* Uncomment if your platform has its own mcuboot_config/mcuboot_assert.h.
|
||||
* If so, it must provide an ASSERT macro for use by bootutil. Otherwise,
|
||||
* "assert" is used. */
|
||||
#define MCUBOOT_HAVE_ASSERT_H 1
|
||||
|
||||
#ifdef CONFIG_ESP_MCUBOOT_SERIAL
|
||||
#define CONFIG_MCUBOOT_SERIAL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When a serial recovery process is receiving the image data, this option
|
||||
* enables it to erase flash progressively (by sectors) instead of the
|
||||
* default behavior that is erasing whole image size of flash area after
|
||||
* receiving first frame.
|
||||
* Enabling this options prevents stalling the beginning of transfer
|
||||
* for the time needed to erase large chunk of flash.
|
||||
*/
|
||||
#ifdef CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY
|
||||
#define MCUBOOT_ERASE_PROGRESSIVELY
|
||||
#endif
|
||||
|
||||
/* Serial extensions are not implemented
|
||||
*/
|
||||
#define MCUBOOT_PERUSER_MGMT_GROUP_ENABLED 0
|
||||
|
||||
#define MCUBOOT_CPU_IDLE() \
|
||||
do { \
|
||||
} while (0)
|
||||
|
||||
#endif /* __MCUBOOT_CONFIG_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt
|
||||
index 9cd035fcf2b..cda2f6dbaf3 100644
|
||||
--- a/drivers/lora/CMakeLists.txt
|
||||
+++ b/drivers/lora/CMakeLists.txt
|
||||
@@ -1,7 +1,10 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
+# ZephCore patch: adds lr11xx driver subdirectory.
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_LORA_SHELL shell.c)
|
||||
|
||||
+add_subdirectory_ifdef(CONFIG_LORA_LR11XX lr11xx)
|
||||
+
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE loramac-node)
|
||||
add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORA_BASICS_MODEM lora-basics-modem)
|
||||
diff --git a/drivers/lora/Kconfig b/drivers/lora/Kconfig
|
||||
index 657bdb9ce28..0ebee4246f3 100644
|
||||
--- a/drivers/lora/Kconfig
|
||||
+++ b/drivers/lora/Kconfig
|
||||
@@ -5,6 +5,7 @@
|
||||
#
|
||||
|
||||
# Top-level configuration file for LORA drivers.
|
||||
+# ZephCore patch: adds lr11xx driver Kconfig.
|
||||
|
||||
menuconfig LORA
|
||||
bool "LoRa drivers"
|
||||
@@ -60,6 +61,7 @@ config LORA_INIT_PRIORITY
|
||||
|
||||
rsource "Kconfig.sx12xx"
|
||||
rsource "Kconfig.rylrxxx"
|
||||
+rsource "lr11xx/Kconfig"
|
||||
rsource "lora-basics-modem/Kconfig"
|
||||
rsource "native/Kconfig"
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
diff --git a/drivers/lora/loramac-node/sx12xx_common.c b/drivers/lora/loramac-node/sx12xx_common.c
|
||||
index 17689720dd2..85186893fc8 100644
|
||||
--- a/drivers/lora/loramac-node/sx12xx_common.c
|
||||
+++ b/drivers/lora/loramac-node/sx12xx_common.c
|
||||
@@ -3,6 +3,9 @@
|
||||
* Copyright (c) 2020 Grinn
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
+ *
|
||||
+ * ZEPHCORE PATCH: Modified sx12xx_ev_rx_error() to notify async callback
|
||||
+ * with NULL data on RX errors (CRC/header). This allows error counting.
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
@@ -38,6 +41,12 @@ static struct sx12xx_data {
|
||||
struct lora_modem_config tx_cfg;
|
||||
atomic_t modem_usage;
|
||||
struct sx12xx_rx_params rx_params;
|
||||
+ /* Fast TX↔RX switching: cache last full config to detect
|
||||
+ * direction-only changes and skip redundant SPI reconfigure. */
|
||||
+ struct lora_modem_config last_cfg;
|
||||
+ bool last_cfg_valid;
|
||||
+ bool tx_configured; /* RadioSetTxConfig has been called with current params */
|
||||
+ bool rx_configured; /* RadioSetRxConfig has been called with current params */
|
||||
} dev_data;
|
||||
|
||||
int __sx12xx_configure_pin(const struct gpio_dt_spec *gpio, gpio_flags_t flags)
|
||||
@@ -179,6 +188,13 @@ static void sx12xx_ev_rx_error(void)
|
||||
if (dev_data.async_rx_cb) {
|
||||
/* Start receiving again */
|
||||
Radio.Rx(0);
|
||||
+ /*
|
||||
+ * ZEPHCORE PATCH: Notify callback with NULL data to indicate
|
||||
+ * RX error (CRC mismatch, header error). This allows the
|
||||
+ * application to count receive errors for diagnostics.
|
||||
+ */
|
||||
+ dev_data.async_rx_cb(dev_data.dev, NULL, 0, 0, 0,
|
||||
+ dev_data.async_user_data);
|
||||
/* Don't run the synchronous code */
|
||||
return;
|
||||
}
|
||||
@@ -195,26 +211,24 @@ static void sx12xx_ev_rx_error(void)
|
||||
/**
|
||||
* @brief Convert Zephyr bandwidth enum to loramac-node bandwidth index
|
||||
*
|
||||
- * The loramac-node library expects bandwidth as an index (0, 1, 2) into its
|
||||
- * internal Bandwidths[] array, not the actual kHz value.
|
||||
- *
|
||||
- * @param bandwidth Zephyr lora_signal_bandwidth enum value
|
||||
- * @param bw_idx Pointer to store the resulting bandwidth index
|
||||
- * @return 0 on success, -EINVAL if bandwidth is not supported
|
||||
+ * The loramac-node library expects bandwidth as an index into its internal
|
||||
+ * Bandwidths[] array: {BW_007, BW_010, BW_015, BW_020, BW_031,
|
||||
+ * BW_041, BW_062, BW_125, BW_250, BW_500}
|
||||
*/
|
||||
static int sx12xx_get_bandwidth_idx(enum lora_signal_bandwidth bandwidth,
|
||||
uint32_t *bw_idx)
|
||||
{
|
||||
switch (bandwidth) {
|
||||
- case BW_125_KHZ:
|
||||
- *bw_idx = 0;
|
||||
- break;
|
||||
- case BW_250_KHZ:
|
||||
- *bw_idx = 1;
|
||||
- break;
|
||||
- case BW_500_KHZ:
|
||||
- *bw_idx = 2;
|
||||
- break;
|
||||
+ case BW_7_KHZ: *bw_idx = 0; break;
|
||||
+ case BW_10_KHZ: *bw_idx = 1; break;
|
||||
+ case BW_15_KHZ: *bw_idx = 2; break;
|
||||
+ case BW_20_KHZ: *bw_idx = 3; break;
|
||||
+ case BW_31_KHZ: *bw_idx = 4; break;
|
||||
+ case BW_41_KHZ: *bw_idx = 5; break;
|
||||
+ case BW_62_KHZ: *bw_idx = 6; break;
|
||||
+ case BW_125_KHZ: *bw_idx = 7; break;
|
||||
+ case BW_250_KHZ: *bw_idx = 8; break;
|
||||
+ case BW_500_KHZ: *bw_idx = 9; break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -225,7 +239,6 @@ uint32_t sx12xx_airtime(const struct device *dev, uint32_t data_len)
|
||||
{
|
||||
uint32_t bw_idx;
|
||||
|
||||
- /* Translate bandwidth to loramac-node index, default to 0 if invalid */
|
||||
if (sx12xx_get_bandwidth_idx(dev_data.tx_cfg.bandwidth, &bw_idx) < 0) {
|
||||
bw_idx = 0;
|
||||
}
|
||||
@@ -375,6 +388,21 @@ int sx12xx_lora_recv_async(const struct device *dev, lora_recv_cb cb, void *user
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* Check if only the TX/RX direction changed (all radio params identical). */
|
||||
+static bool sx12xx_only_direction_changed(const struct lora_modem_config *a,
|
||||
+ const struct lora_modem_config *b)
|
||||
+{
|
||||
+ return a->frequency == b->frequency &&
|
||||
+ a->bandwidth == b->bandwidth &&
|
||||
+ a->datarate == b->datarate &&
|
||||
+ a->coding_rate == b->coding_rate &&
|
||||
+ a->preamble_len == b->preamble_len &&
|
||||
+ a->tx_power == b->tx_power &&
|
||||
+ a->iq_inverted == b->iq_inverted &&
|
||||
+ a->public_network == b->public_network &&
|
||||
+ a->tx != b->tx;
|
||||
+}
|
||||
+
|
||||
int sx12xx_lora_config(const struct device *dev,
|
||||
struct lora_modem_config *config)
|
||||
{
|
||||
@@ -388,6 +416,39 @@ int sx12xx_lora_config(const struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ /* Fast path: if only TX↔RX direction changed and the target direction
|
||||
+ * was already configured once with the same params, skip the full
|
||||
+ * RadioSetTxConfig/RadioSetRxConfig (saves ~35ms of SPI traffic).
|
||||
+ * RadioSetTxConfig MUST have been called at least once to set
|
||||
+ * TxTimeout=4000; RadioSetRxConfig MUST have been called at least
|
||||
+ * once to set PayloadLength/SymbTimeout/IQ polarity workaround. */
|
||||
+ if (dev_data.last_cfg_valid &&
|
||||
+ sx12xx_only_direction_changed(config, &dev_data.last_cfg)) {
|
||||
+ if (config->tx && dev_data.tx_configured) {
|
||||
+ LOG_DBG("lora_config: fast TX switch (skip full reconfig)");
|
||||
+ if (!modem_acquire(&dev_data)) {
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ memcpy(&dev_data.tx_cfg, config, sizeof(dev_data.tx_cfg));
|
||||
+ dev_data.last_cfg = *config;
|
||||
+ modem_release(&dev_data);
|
||||
+ return 0;
|
||||
+ }
|
||||
+ if (!config->tx && dev_data.rx_configured) {
|
||||
+ LOG_DBG("lora_config: fast RX switch (skip full reconfig)");
|
||||
+ if (!modem_acquire(&dev_data)) {
|
||||
+ return -EBUSY;
|
||||
+ }
|
||||
+ dev_data.last_cfg = *config;
|
||||
+ modem_release(&dev_data);
|
||||
+ return 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LOG_INF("lora_config: bw_enum=%d bw_idx=%u tx=%d freq=%u sf=%d",
|
||||
+ config->bandwidth, bw_idx, config->tx, config->frequency,
|
||||
+ config->datarate);
|
||||
+
|
||||
/* Ensure available, decremented after configuration */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
@@ -403,16 +464,21 @@ int sx12xx_lora_config(const struct device *dev,
|
||||
bw_idx, config->datarate,
|
||||
config->coding_rate, config->preamble_len,
|
||||
false, crc, 0, 0, config->iq_inverted, 4000);
|
||||
+ dev_data.tx_configured = true;
|
||||
} else {
|
||||
/* TODO: Get symbol timeout value from config parameters */
|
||||
Radio.SetRxConfig(MODEM_LORA, bw_idx,
|
||||
config->datarate, config->coding_rate,
|
||||
0, config->preamble_len, 10, false, 0,
|
||||
crc, false, 0, config->iq_inverted, true);
|
||||
+ dev_data.rx_configured = true;
|
||||
}
|
||||
|
||||
Radio.SetPublicNetwork(config->public_network);
|
||||
|
||||
+ dev_data.last_cfg = *config;
|
||||
+ dev_data.last_cfg_valid = true;
|
||||
+
|
||||
modem_release(&dev_data);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c
|
||||
index 8e0ca45c271..828faaaf37f 100644
|
||||
--- a/drivers/lora/native/sx126x/sx126x.c
|
||||
+++ b/drivers/lora/native/sx126x/sx126x.c
|
||||
@@ -451,7 +451,14 @@ static void sx126x_set_rf_path(const struct device *dev, bool enable, bool tx)
|
||||
const struct sx126x_hal_config *config = dev->config;
|
||||
|
||||
sx126x_hal_set_antenna_enable(dev, enable);
|
||||
- if (!config->dio2_tx_enable) {
|
||||
+ if (config->dio2_tx_enable) {
|
||||
+ /* DIO2 handles TX enable in hardware — but rx-enable-gpios
|
||||
+ * (e.g. E22-900M30S RXEN) still needs explicit GPIO control.
|
||||
+ * RXEN HIGH when receiving, LOW otherwise. */
|
||||
+ if (config->rx_enable.port != NULL) {
|
||||
+ gpio_pin_set_dt(&config->rx_enable, enable && !tx);
|
||||
+ }
|
||||
+ } else {
|
||||
sx126x_hal_set_rf_switch(dev, enable && tx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
diff --git a/drivers/lora/loramac-node/sx126x_standalone.c b/drivers/lora/loramac-node/sx126x_standalone.c
|
||||
index c9b16965bc7..3a2ff8b20c9 100644
|
||||
--- a/drivers/lora/loramac-node/sx126x_standalone.c
|
||||
+++ b/drivers/lora/loramac-node/sx126x_standalone.c
|
||||
@@ -40,8 +40,11 @@ uint32_t sx126x_get_dio1_pin_state(struct sx126x_data *dev_data)
|
||||
|
||||
void sx126x_dio1_irq_enable(struct sx126x_data *dev_data)
|
||||
{
|
||||
- gpio_pin_interrupt_configure_dt(&sx126x_gpio_dio1,
|
||||
+ int ret = gpio_pin_interrupt_configure_dt(&sx126x_gpio_dio1,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
+ if (ret != 0) {
|
||||
+ LOG_ERR("DIO1 irq enable FAILED: %d", ret);
|
||||
+ }
|
||||
}
|
||||
|
||||
void sx126x_dio1_irq_disable(struct sx126x_data *dev_data)
|
||||
@@ -0,0 +1,119 @@
|
||||
diff --git a/drivers/gnss/Kconfig.luatos_air530z b/drivers/gnss/Kconfig.luatos_air530z
|
||||
index c5d09261da5..ffc218b22e2 100644
|
||||
--- a/drivers/gnss/Kconfig.luatos_air530z
|
||||
+++ b/drivers/gnss/Kconfig.luatos_air530z
|
||||
@@ -28,4 +28,16 @@ config GNSS_LUATOS_AIR530Z_SATELLITES_COUNT
|
||||
the device is actually tracking, just how many of those can
|
||||
be reported in the satellites callback.
|
||||
|
||||
+config GNSS_LUATOS_AIR530Z_EASY
|
||||
+ bool "Enable EASY (Embedded Assist System) for faster TTFF"
|
||||
+ default y
|
||||
+ help
|
||||
+ Enable MediaTek EASY (Embedded Assist System) mode via PMTK869
|
||||
+ command. EASY caches predicted ephemeris in the GNSS module's
|
||||
+ internal flash, reducing Time-To-First-Fix from 15-45s (cold)
|
||||
+ to 1-3s (warm). Sent on every driver init (boot and PM resume).
|
||||
+ The setting persists in GNSS flash, so resending is a no-op.
|
||||
+ Compatible with L76K/L76KB and other MediaTek-based GNSS chips
|
||||
+ that accept PMTK commands alongside PCAS.
|
||||
+
|
||||
endif
|
||||
diff --git a/drivers/gnss/gnss_luatos_air530z.c b/drivers/gnss/gnss_luatos_air530z.c
|
||||
index 74708edbd62..7de70ad93c8 100644
|
||||
--- a/drivers/gnss/gnss_luatos_air530z.c
|
||||
+++ b/drivers/gnss/gnss_luatos_air530z.c
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <zephyr/modem/chat.h>
|
||||
#include <zephyr/modem/backend/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
-#include <zephyr/pm/device.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -36,6 +35,13 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE(init_script_cmds,
|
||||
/* receive only GGA and RMC NMEA messages */
|
||||
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PCAS03,1,0,0,0,1,0,0,0,0,0,0,0,0*1E", 10),
|
||||
#endif
|
||||
+#if IS_ENABLED(CONFIG_GNSS_LUATOS_AIR530Z_EASY)
|
||||
+ /* Enable EASY (Embedded Assist System) — caches predicted ephemeris
|
||||
+ * in GNSS internal flash for 1-3s warm start instead of 15-45s cold.
|
||||
+ * PMTK869,1,1 = Set EASY, Enable. Persists across power cycles.
|
||||
+ * L76K/L76KB accept PMTK alongside PCAS (both MediaTek MT33xx). */
|
||||
+ MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PMTK869,1,1*36", 10),
|
||||
+#endif
|
||||
);
|
||||
|
||||
MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(init_script, init_script_cmds, NULL, 5);
|
||||
@@ -217,54 +223,11 @@ static int gnss_luatos_air530z_init(const struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int luatos_air530z_pm_resume(const struct device *dev)
|
||||
-{
|
||||
- struct gnss_luatos_air530z_data *data = dev->data;
|
||||
- int ret;
|
||||
-
|
||||
- ret = modem_pipe_open(data->uart_pipe, K_SECONDS(10));
|
||||
- if (ret < 0) {
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- ret = modem_chat_attach(&data->chat, data->uart_pipe);
|
||||
- if (ret < 0) {
|
||||
- modem_pipe_close(data->uart_pipe, K_SECONDS(10));
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- ret = modem_chat_run_script(&data->chat, &init_script);
|
||||
- if (ret < 0) {
|
||||
- modem_pipe_close(data->uart_pipe, K_SECONDS(10));
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int luatos_air530z_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
-{
|
||||
- struct gnss_luatos_air530z_data *data = dev->data;
|
||||
- const struct gnss_luatos_air530z_config *config = dev->config;
|
||||
- int ret = -ENOTSUP;
|
||||
-
|
||||
- switch (action) {
|
||||
- case PM_DEVICE_ACTION_SUSPEND:
|
||||
- gpio_pin_set_dt(&config->on_off_gpio, 0);
|
||||
- ret = modem_pipe_close(data->uart_pipe, K_SECONDS(10));
|
||||
- break;
|
||||
-
|
||||
- case PM_DEVICE_ACTION_RESUME:
|
||||
- gpio_pin_set_dt(&config->on_off_gpio, 1);
|
||||
- ret = luatos_air530z_pm_resume(dev);
|
||||
- break;
|
||||
-
|
||||
- default:
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
+/* PM intentionally removed — the L76K ignores $PMTK161,0 (AT6558-based,
|
||||
+ * not genuine MediaTek). GPS standby uses GPIO FORCE_ON pin instead.
|
||||
+ * Also, CONFIG_PM_DEVICE_SYSTEM_MANAGED can auto-suspend devices during
|
||||
+ * idle, calling modem_chat_run_script() from an unexpected context and
|
||||
+ * potentially deadlocking the system. */
|
||||
|
||||
static int luatos_air530z_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
|
||||
{
|
||||
@@ -356,10 +319,8 @@ static DEVICE_API(gnss, gnss_api) = {
|
||||
.dynamic_separators_buf = {',', '*'}, \
|
||||
}; \
|
||||
\
|
||||
- PM_DEVICE_DT_INST_DEFINE(inst, luatos_air530z_pm_action); \
|
||||
- \
|
||||
DEVICE_DT_INST_DEFINE(inst, gnss_luatos_air530z_init, \
|
||||
- PM_DEVICE_DT_INST_GET(inst), \
|
||||
+ NULL, \
|
||||
&gnss_luatos_air530z_data_##inst, \
|
||||
&gnss_luatos_air530z_cfg_##inst, \
|
||||
POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api);
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/scripts/west_commands/blobs.py b/scripts/west_commands/blobs.py
|
||||
index 14f0412cda5..615ba602a6b 100644
|
||||
--- a/scripts/west_commands/blobs.py
|
||||
+++ b/scripts/west_commands/blobs.py
|
||||
@@ -288,7 +288,7 @@ class Blobs(WestCommand):
|
||||
continue
|
||||
self.inf(f"Fetching blob {blob['module']}: {blob['abspath']}")
|
||||
|
||||
- if blob['click-through'] and not args.auto_accept:
|
||||
+ if blob.get('click-through') and not args.auto_accept:
|
||||
while True:
|
||||
user_input = input(
|
||||
"For this blob, need to read and accept "
|
||||
@@ -1,43 +0,0 @@
|
||||
# Copyright 2024 Jerónimo Agulló
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config GNSS_LUATOS_AIR530Z
|
||||
bool "Luatos Air530z GNSS device"
|
||||
default y
|
||||
depends on GNSS
|
||||
depends on DT_HAS_LUATOS_AIR530Z_ENABLED
|
||||
depends on GNSS_REFERENCE_FRAME_WGS84
|
||||
select MODEM_MODULES
|
||||
select MODEM_BACKEND_UART
|
||||
select MODEM_CHAT
|
||||
select GNSS_PARSE
|
||||
select GNSS_NMEA0183
|
||||
select GNSS_NMEA0183_MATCH
|
||||
help
|
||||
Enable Luatos Air530z GNSS driver.
|
||||
|
||||
if GNSS_LUATOS_AIR530Z
|
||||
|
||||
config GNSS_LUATOS_AIR530Z_SATELLITES_COUNT
|
||||
int "Maximum satellite count"
|
||||
depends on GNSS_SATELLITES
|
||||
default 24
|
||||
help
|
||||
Maximum number of satellites that can be decoded from the
|
||||
GNSS device. This does not affect the number of devices that
|
||||
the device is actually tracking, just how many of those can
|
||||
be reported in the satellites callback.
|
||||
|
||||
config GNSS_LUATOS_AIR530Z_EASY
|
||||
bool "Enable EASY (Embedded Assist System) for faster TTFF"
|
||||
default y
|
||||
help
|
||||
Enable MediaTek EASY (Embedded Assist System) mode via PMTK869
|
||||
command. EASY caches predicted ephemeris in the GNSS module's
|
||||
internal flash, reducing Time-To-First-Fix from 15-45s (cold)
|
||||
to 1-3s (warm). Sent on every driver init (boot and PM resume).
|
||||
The setting persists in GNSS flash, so resending is a no-op.
|
||||
Compatible with L76K/L76KB and other MediaTek-based GNSS chips
|
||||
that accept PMTK commands alongside PCAS.
|
||||
|
||||
endif
|
||||
@@ -1,328 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Jerónimo Agulló
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gnss.h>
|
||||
#include <zephyr/drivers/gnss/gnss_publish.h>
|
||||
#include <zephyr/modem/chat.h>
|
||||
#include <zephyr/modem/backend/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gnss_nmea0183.h"
|
||||
#include "gnss_nmea0183_match.h"
|
||||
#include "gnss_parse.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(luatos_air530z, CONFIG_GNSS_LOG_LEVEL);
|
||||
|
||||
#define DT_DRV_COMPAT luatos_air530z
|
||||
|
||||
#define UART_RECV_BUF_SZ 128
|
||||
#define UART_TRANS_BUF_SZ 64
|
||||
|
||||
#define CHAT_RECV_BUF_SZ 256
|
||||
#define CHAT_ARGV_SZ 32
|
||||
|
||||
MODEM_CHAT_SCRIPT_CMDS_DEFINE(init_script_cmds,
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
/* receive only GGA, RMC and GSV NMEA messages */
|
||||
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PCAS03,1,0,0,1,1,0,0,0,0,0,0,0,0*1F", 10),
|
||||
#else
|
||||
/* receive only GGA and RMC NMEA messages */
|
||||
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PCAS03,1,0,0,0,1,0,0,0,0,0,0,0,0*1E", 10),
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_GNSS_LUATOS_AIR530Z_EASY)
|
||||
/* Enable EASY (Embedded Assist System) — caches predicted ephemeris
|
||||
* in GNSS internal flash for 1-3s warm start instead of 15-45s cold.
|
||||
* PMTK869,1,1 = Set EASY, Enable. Persists across power cycles.
|
||||
* L76K/L76KB accept PMTK alongside PCAS (both MediaTek MT33xx). */
|
||||
MODEM_CHAT_SCRIPT_CMD_RESP_NONE("$PMTK869,1,1*36", 10),
|
||||
#endif
|
||||
);
|
||||
|
||||
MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(init_script, init_script_cmds, NULL, 5);
|
||||
|
||||
struct gnss_luatos_air530z_config {
|
||||
const struct device *uart;
|
||||
const struct gpio_dt_spec on_off_gpio;
|
||||
const int uart_baudrate;
|
||||
};
|
||||
|
||||
struct gnss_luatos_air530z_data {
|
||||
struct gnss_nmea0183_match_data match_data;
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
struct gnss_satellite satellites[CONFIG_GNSS_LUATOS_AIR530Z_SATELLITES_COUNT];
|
||||
#endif
|
||||
|
||||
/* UART backend */
|
||||
struct modem_pipe *uart_pipe;
|
||||
struct modem_backend_uart uart_backend;
|
||||
uint8_t uart_backend_receive_buf[UART_RECV_BUF_SZ];
|
||||
uint8_t uart_backend_transmit_buf[UART_TRANS_BUF_SZ];
|
||||
|
||||
/* Modem chat */
|
||||
struct modem_chat chat;
|
||||
uint8_t chat_receive_buf[CHAT_RECV_BUF_SZ];
|
||||
uint8_t chat_delimiter[2];
|
||||
uint8_t *chat_argv[CHAT_ARGV_SZ];
|
||||
|
||||
/* Dynamic chat script */
|
||||
uint8_t dynamic_separators_buf[2];
|
||||
uint8_t dynamic_request_buf[32];
|
||||
struct modem_chat_script_chat dynamic_script_chat;
|
||||
struct modem_chat_script dynamic_script;
|
||||
|
||||
struct k_sem lock;
|
||||
};
|
||||
|
||||
MODEM_CHAT_MATCHES_DEFINE(unsol_matches,
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GGA,", ",*", gnss_nmea0183_match_gga_callback),
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??RMC,", ",*", gnss_nmea0183_match_rmc_callback),
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
MODEM_CHAT_MATCH_WILDCARD("$??GSV,", ",*", gnss_nmea0183_match_gsv_callback),
|
||||
#endif
|
||||
);
|
||||
|
||||
static void luatos_air530z_lock(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
(void)k_sem_take(&data->lock, K_FOREVER);
|
||||
}
|
||||
|
||||
static void luatos_air530z_unlock(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
k_sem_give(&data->lock);
|
||||
}
|
||||
|
||||
static int gnss_luatos_air530z_init_nmea0183_match(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
const struct gnss_nmea0183_match_config match_config = {
|
||||
.gnss = dev,
|
||||
#if CONFIG_GNSS_SATELLITES
|
||||
.satellites = data->satellites,
|
||||
.satellites_size = ARRAY_SIZE(data->satellites),
|
||||
#endif
|
||||
};
|
||||
|
||||
return gnss_nmea0183_match_init(&data->match_data, &match_config);
|
||||
}
|
||||
|
||||
static void gnss_luatos_air530z_init_pipe(const struct device *dev)
|
||||
{
|
||||
const struct gnss_luatos_air530z_config *config = dev->config;
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
const struct modem_backend_uart_config uart_backend_config = {
|
||||
.uart = config->uart,
|
||||
.receive_buf = data->uart_backend_receive_buf,
|
||||
.receive_buf_size = sizeof(data->uart_backend_receive_buf),
|
||||
.transmit_buf = data->uart_backend_transmit_buf,
|
||||
.transmit_buf_size = ARRAY_SIZE(data->uart_backend_transmit_buf),
|
||||
};
|
||||
|
||||
data->uart_pipe = modem_backend_uart_init(&data->uart_backend, &uart_backend_config);
|
||||
}
|
||||
|
||||
static int gnss_luatos_air530z_init_chat(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
const struct modem_chat_config chat_config = {
|
||||
.user_data = data,
|
||||
.receive_buf = data->chat_receive_buf,
|
||||
.receive_buf_size = sizeof(data->chat_receive_buf),
|
||||
.delimiter = data->chat_delimiter,
|
||||
.delimiter_size = ARRAY_SIZE(data->chat_delimiter),
|
||||
.filter = NULL,
|
||||
.filter_size = 0,
|
||||
.argv = data->chat_argv,
|
||||
.argv_size = ARRAY_SIZE(data->chat_argv),
|
||||
.unsol_matches = unsol_matches,
|
||||
.unsol_matches_size = ARRAY_SIZE(unsol_matches),
|
||||
};
|
||||
|
||||
return modem_chat_init(&data->chat, &chat_config);
|
||||
}
|
||||
|
||||
static void luatos_air530z_init_dynamic_script(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
|
||||
/* Air530z doesn't respond to commands. Thus, response_matches_size = 0; */
|
||||
data->dynamic_script_chat.request = data->dynamic_request_buf;
|
||||
data->dynamic_script_chat.response_matches = NULL;
|
||||
data->dynamic_script_chat.response_matches_size = 0;
|
||||
data->dynamic_script_chat.timeout = 0;
|
||||
|
||||
data->dynamic_script.name = "PCAS";
|
||||
data->dynamic_script.script_chats = &data->dynamic_script_chat;
|
||||
data->dynamic_script.script_chats_size = 1;
|
||||
data->dynamic_script.abort_matches = NULL;
|
||||
data->dynamic_script.abort_matches_size = 0;
|
||||
data->dynamic_script.callback = NULL;
|
||||
data->dynamic_script.timeout = 5;
|
||||
}
|
||||
|
||||
static int gnss_luatos_air530z_init(const struct device *dev)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
const struct gnss_luatos_air530z_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
k_sem_init(&data->lock, 1, 1);
|
||||
|
||||
ret = gnss_luatos_air530z_init_nmea0183_match(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
gnss_luatos_air530z_init_pipe(dev);
|
||||
|
||||
ret = gnss_luatos_air530z_init_chat(dev);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
luatos_air530z_init_dynamic_script(dev);
|
||||
|
||||
ret = modem_pipe_open(data->uart_pipe, K_SECONDS(10));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = modem_chat_attach(&data->chat, data->uart_pipe);
|
||||
if (ret < 0) {
|
||||
modem_pipe_close(data->uart_pipe, K_SECONDS(10));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = modem_chat_run_script(&data->chat, &init_script);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to run init_script");
|
||||
modem_pipe_close(data->uart_pipe, K_SECONDS(10));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* setup on-off gpio for power management */
|
||||
if (!gpio_is_ready_dt(&config->on_off_gpio)) {
|
||||
LOG_ERR("on-off GPIO device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure_dt(&config->on_off_gpio, GPIO_OUTPUT_HIGH);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PM intentionally removed — the L76K ignores $PMTK161,0 (AT6558-based,
|
||||
* not genuine MediaTek). GPS standby uses GPIO FORCE_ON pin instead.
|
||||
* Also, CONFIG_PM_DEVICE_SYSTEM_MANAGED can auto-suspend devices during
|
||||
* idle, calling modem_chat_run_script() from an unexpected context and
|
||||
* potentially deadlocking the system. */
|
||||
|
||||
static int luatos_air530z_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
int ret;
|
||||
|
||||
if (fix_interval_ms < 100 || fix_interval_ms > 1000) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
luatos_air530z_lock(dev);
|
||||
|
||||
ret = gnss_nmea0183_snprintk(data->dynamic_request_buf, sizeof(data->dynamic_request_buf),
|
||||
"PCAS02,%u", fix_interval_ms);
|
||||
if (ret < 0) {
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
data->dynamic_script_chat.request_size = ret;
|
||||
|
||||
ret = modem_chat_run_script(&data->chat, &data->dynamic_script);
|
||||
if (ret < 0) {
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
unlock_return:
|
||||
luatos_air530z_unlock(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int luatos_air530z_set_enabled_systems(const struct device *dev, gnss_systems_t systems)
|
||||
{
|
||||
struct gnss_luatos_air530z_data *data = dev->data;
|
||||
gnss_systems_t supported_systems;
|
||||
uint8_t encoded_systems = 0;
|
||||
int ret;
|
||||
|
||||
supported_systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_BEIDOU);
|
||||
|
||||
if ((~supported_systems) & systems) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
luatos_air530z_lock(dev);
|
||||
|
||||
WRITE_BIT(encoded_systems, 0, systems & GNSS_SYSTEM_GPS);
|
||||
WRITE_BIT(encoded_systems, 1, systems & GNSS_SYSTEM_GLONASS);
|
||||
WRITE_BIT(encoded_systems, 2, systems & GNSS_SYSTEM_BEIDOU);
|
||||
|
||||
ret = gnss_nmea0183_snprintk(data->dynamic_request_buf, sizeof(data->dynamic_request_buf),
|
||||
"PCAS04,%u", encoded_systems);
|
||||
if (ret < 0) {
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
data->dynamic_script_chat.request_size = ret;
|
||||
|
||||
ret = modem_chat_run_script(&data->chat, &data->dynamic_script);
|
||||
if (ret < 0) {
|
||||
goto unlock_return;
|
||||
}
|
||||
|
||||
unlock_return:
|
||||
luatos_air530z_unlock(dev);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int luatos_air530z_get_supported_systems(const struct device *dev, gnss_systems_t *systems)
|
||||
{
|
||||
*systems = (GNSS_SYSTEM_GPS | GNSS_SYSTEM_GLONASS | GNSS_SYSTEM_BEIDOU);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_API(gnss, gnss_api) = {
|
||||
.set_fix_rate = luatos_air530z_set_fix_rate,
|
||||
.set_enabled_systems = luatos_air530z_set_enabled_systems,
|
||||
.get_supported_systems = luatos_air530z_get_supported_systems,
|
||||
};
|
||||
|
||||
#define LUATOS_AIR530Z(inst) \
|
||||
static const struct gnss_luatos_air530z_config gnss_luatos_air530z_cfg_##inst = { \
|
||||
.uart = DEVICE_DT_GET(DT_INST_BUS(inst)), \
|
||||
.on_off_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, on_off_gpios, { 0 }), \
|
||||
}; \
|
||||
\
|
||||
static struct gnss_luatos_air530z_data gnss_luatos_air530z_data_##inst = { \
|
||||
.chat_delimiter = {'\r', '\n'}, \
|
||||
.dynamic_separators_buf = {',', '*'}, \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, gnss_luatos_air530z_init, \
|
||||
NULL, \
|
||||
&gnss_luatos_air530z_data_##inst, \
|
||||
&gnss_luatos_air530z_cfg_##inst, \
|
||||
POST_KERNEL, CONFIG_GNSS_INIT_PRIORITY, &gnss_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(LUATOS_AIR530Z)
|
||||
@@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# ZephCore patch: adds lr11xx driver subdirectory.
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_LORA_SHELL shell.c)
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_LORA_LR11XX lr11xx)
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORAMAC_NODE loramac-node)
|
||||
add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_LORA_BASICS_MODEM lora-basics-modem)
|
||||
add_subdirectory_ifdef(CONFIG_LORA_MODULE_BACKEND_NATIVE native)
|
||||
# zephyr-keep-sorted-stop
|
||||
@@ -1,68 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2019 Manivannan Sadhasivam
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# Top-level configuration file for LORA drivers.
|
||||
# ZephCore patch: adds lr11xx driver Kconfig.
|
||||
|
||||
menuconfig LORA
|
||||
bool "LoRa drivers"
|
||||
select POLL
|
||||
help
|
||||
Include LoRa drivers in the system configuration.
|
||||
|
||||
if LORA
|
||||
|
||||
choice LORA_MODULE_BACKEND
|
||||
prompt "Low-level LoRa modem integration to use"
|
||||
default LORA_MODULE_BACKEND_LORA_BASICS_MODEM if DT_HAS_SEMTECH_SX1268_ENABLED \
|
||||
|| DT_HAS_SEMTECH_LLCC68_ENABLED || DT_HAS_SEMTECH_SX1278_ENABLED
|
||||
|
||||
config LORA_MODULE_BACKEND_LORAMAC_NODE
|
||||
bool "loramac-node backend"
|
||||
depends on ZEPHYR_LORAMAC_NODE_MODULE
|
||||
|
||||
config LORA_MODULE_BACKEND_LORA_BASICS_MODEM
|
||||
bool "LoRa Basic modem backend"
|
||||
depends on ZEPHYR_LORA_BASICS_MODEM_MODULE
|
||||
depends on DT_HAS_SEMTECH_SX1262_ENABLED || DT_HAS_SEMTECH_SX1261_ENABLED \
|
||||
|| DT_HAS_SEMTECH_SX1272_ENABLED || DT_HAS_SEMTECH_SX1276_ENABLED \
|
||||
|| DT_HAS_SEMTECH_SX1268_ENABLED || DT_HAS_SEMTECH_LLCC68_ENABLED \
|
||||
|| DT_HAS_SEMTECH_SX1278_ENABLED
|
||||
select USE_LORA_BASICS_MODEM_DRIVERS
|
||||
help
|
||||
LoRa API support using the LoRa Basics Modem module.
|
||||
|
||||
config LORA_MODULE_BACKEND_NATIVE
|
||||
bool "Native Zephyr backend"
|
||||
help
|
||||
Use Zephyr's built-in LoRa drivers without external modules.
|
||||
Currently supports SX1261/SX1262 transceivers.
|
||||
|
||||
endchoice
|
||||
|
||||
module = LORA
|
||||
module-str = lora
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
config LORA_SHELL
|
||||
bool "LoRa Shell"
|
||||
depends on SHELL
|
||||
help
|
||||
Enable LoRa Shell for testing.
|
||||
|
||||
config LORA_INIT_PRIORITY
|
||||
int "LoRa initialization priority"
|
||||
default 90
|
||||
help
|
||||
System initialization priority for LoRa drivers.
|
||||
|
||||
rsource "Kconfig.sx12xx"
|
||||
rsource "Kconfig.rylrxxx"
|
||||
rsource "lr11xx/Kconfig"
|
||||
rsource "lora-basics-modem/Kconfig"
|
||||
rsource "native/Kconfig"
|
||||
|
||||
endif # LORA
|
||||
@@ -1,521 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Manivannan Sadhasivam
|
||||
* Copyright (c) 2020 Grinn
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* ZEPHCORE PATCH: Modified sx12xx_ev_rx_error() to notify async callback
|
||||
* with NULL data on RX errors (CRC/header). This allows error counting.
|
||||
*/
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/lora.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/atomic.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
/* LoRaMac-node specific includes */
|
||||
#include <radio.h>
|
||||
|
||||
#include "sx12xx_common.h"
|
||||
|
||||
#define STATE_FREE 0
|
||||
#define STATE_BUSY 1
|
||||
#define STATE_CLEANUP 2
|
||||
|
||||
LOG_MODULE_REGISTER(sx12xx_common, CONFIG_LORA_LOG_LEVEL);
|
||||
|
||||
struct sx12xx_rx_params {
|
||||
uint8_t *buf;
|
||||
uint8_t *size;
|
||||
int16_t *rssi;
|
||||
int8_t *snr;
|
||||
};
|
||||
|
||||
static struct sx12xx_data {
|
||||
const struct device *dev;
|
||||
struct k_poll_signal *operation_done;
|
||||
lora_recv_cb async_rx_cb;
|
||||
void *async_user_data;
|
||||
RadioEvents_t events;
|
||||
struct lora_modem_config tx_cfg;
|
||||
atomic_t modem_usage;
|
||||
struct sx12xx_rx_params rx_params;
|
||||
/* Fast TX↔RX switching: cache last full config to detect
|
||||
* direction-only changes and skip redundant SPI reconfigure. */
|
||||
struct lora_modem_config last_cfg;
|
||||
bool last_cfg_valid;
|
||||
bool tx_configured; /* RadioSetTxConfig has been called with current params */
|
||||
bool rx_configured; /* RadioSetRxConfig has been called with current params */
|
||||
} dev_data;
|
||||
|
||||
int __sx12xx_configure_pin(const struct gpio_dt_spec *gpio, gpio_flags_t flags)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!device_is_ready(gpio->port)) {
|
||||
LOG_ERR("GPIO device not ready %s", gpio->port->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = gpio_pin_configure_dt(gpio, flags);
|
||||
if (err) {
|
||||
LOG_ERR("Cannot configure gpio %s %d: %d", gpio->port->name,
|
||||
gpio->pin, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempt to acquire the modem for operations
|
||||
*
|
||||
* @param data common sx12xx data struct
|
||||
*
|
||||
* @retval true if modem was acquired
|
||||
* @retval false otherwise
|
||||
*/
|
||||
static inline bool modem_acquire(struct sx12xx_data *data)
|
||||
{
|
||||
return atomic_cas(&data->modem_usage, STATE_FREE, STATE_BUSY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Safely release the modem from any context
|
||||
*
|
||||
* This function can be called from any context and guarantees that the
|
||||
* release operations will only be run once.
|
||||
*
|
||||
* @param data common sx12xx data struct
|
||||
*
|
||||
* @retval true if modem was released by this function
|
||||
* @retval false otherwise
|
||||
*/
|
||||
static bool modem_release(struct sx12xx_data *data)
|
||||
{
|
||||
/* Increment atomic so both acquire and release will fail */
|
||||
if (!atomic_cas(&data->modem_usage, STATE_BUSY, STATE_CLEANUP)) {
|
||||
return false;
|
||||
}
|
||||
/* Put radio back into sleep mode */
|
||||
Radio.Sleep();
|
||||
/* Completely release modem */
|
||||
data->operation_done = NULL;
|
||||
atomic_clear(&data->modem_usage);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sx12xx_ev_rx_done(uint8_t *payload, uint16_t size, int16_t rssi,
|
||||
int8_t snr)
|
||||
{
|
||||
struct k_poll_signal *sig = dev_data.operation_done;
|
||||
|
||||
/* Receiving in asynchronous mode */
|
||||
if (dev_data.async_rx_cb) {
|
||||
/* Start receiving again */
|
||||
Radio.Rx(0);
|
||||
/* Run the callback */
|
||||
dev_data.async_rx_cb(dev_data.dev, payload, size, rssi, snr,
|
||||
dev_data.async_user_data);
|
||||
/* Don't run the synchronous code */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Manually release the modem instead of just calling modem_release
|
||||
* as we need to perform cleanup operations while still ensuring
|
||||
* others can't use the modem.
|
||||
*/
|
||||
if (!atomic_cas(&dev_data.modem_usage, STATE_BUSY, STATE_CLEANUP)) {
|
||||
return;
|
||||
}
|
||||
/* We can make two observations here:
|
||||
* 1. lora_recv hasn't already exited due to a timeout.
|
||||
* (modem_release would have been successfully called)
|
||||
* 2. If the k_poll in lora_recv times out before we raise the signal,
|
||||
* but while this code is running, it will block on the
|
||||
* signal again.
|
||||
* This lets us guarantee that the operation_done signal and pointers
|
||||
* in rx_params are always valid in this function.
|
||||
*/
|
||||
|
||||
/* Store actual size */
|
||||
if (size < *dev_data.rx_params.size) {
|
||||
*dev_data.rx_params.size = size;
|
||||
}
|
||||
/* Copy received data to output buffer */
|
||||
memcpy(dev_data.rx_params.buf, payload,
|
||||
*dev_data.rx_params.size);
|
||||
/* Output RSSI and SNR */
|
||||
if (dev_data.rx_params.rssi) {
|
||||
*dev_data.rx_params.rssi = rssi;
|
||||
}
|
||||
if (dev_data.rx_params.snr) {
|
||||
*dev_data.rx_params.snr = snr;
|
||||
}
|
||||
/* Put radio back into sleep mode */
|
||||
Radio.Sleep();
|
||||
/* Completely release modem */
|
||||
dev_data.operation_done = NULL;
|
||||
atomic_clear(&dev_data.modem_usage);
|
||||
/* Notify caller RX is complete */
|
||||
k_poll_signal_raise(sig, 0);
|
||||
}
|
||||
|
||||
static void sx12xx_ev_tx_done(void)
|
||||
{
|
||||
struct k_poll_signal *sig = dev_data.operation_done;
|
||||
|
||||
if (modem_release(&dev_data)) {
|
||||
/* Raise signal if provided */
|
||||
if (sig) {
|
||||
k_poll_signal_raise(sig, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sx12xx_ev_tx_timed_out(void)
|
||||
{
|
||||
/* Just release the modem */
|
||||
modem_release(&dev_data);
|
||||
}
|
||||
|
||||
static void sx12xx_ev_rx_error(void)
|
||||
{
|
||||
struct k_poll_signal *sig = dev_data.operation_done;
|
||||
|
||||
/* Receiving in asynchronous mode */
|
||||
if (dev_data.async_rx_cb) {
|
||||
/* Start receiving again */
|
||||
Radio.Rx(0);
|
||||
/*
|
||||
* ZEPHCORE PATCH: Notify callback with NULL data to indicate
|
||||
* RX error (CRC mismatch, header error). This allows the
|
||||
* application to count receive errors for diagnostics.
|
||||
*/
|
||||
dev_data.async_rx_cb(dev_data.dev, NULL, 0, 0, 0,
|
||||
dev_data.async_user_data);
|
||||
/* Don't run the synchronous code */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finish synchronous receive with error */
|
||||
if (modem_release(&dev_data)) {
|
||||
/* Raise signal if provided */
|
||||
if (sig) {
|
||||
k_poll_signal_raise(sig, -EIO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert Zephyr bandwidth enum to loramac-node bandwidth index
|
||||
*
|
||||
* The loramac-node library expects bandwidth as an index into its internal
|
||||
* Bandwidths[] array: {BW_007, BW_010, BW_015, BW_020, BW_031,
|
||||
* BW_041, BW_062, BW_125, BW_250, BW_500}
|
||||
*/
|
||||
static int sx12xx_get_bandwidth_idx(enum lora_signal_bandwidth bandwidth,
|
||||
uint32_t *bw_idx)
|
||||
{
|
||||
switch (bandwidth) {
|
||||
case BW_7_KHZ: *bw_idx = 0; break;
|
||||
case BW_10_KHZ: *bw_idx = 1; break;
|
||||
case BW_15_KHZ: *bw_idx = 2; break;
|
||||
case BW_20_KHZ: *bw_idx = 3; break;
|
||||
case BW_31_KHZ: *bw_idx = 4; break;
|
||||
case BW_41_KHZ: *bw_idx = 5; break;
|
||||
case BW_62_KHZ: *bw_idx = 6; break;
|
||||
case BW_125_KHZ: *bw_idx = 7; break;
|
||||
case BW_250_KHZ: *bw_idx = 8; break;
|
||||
case BW_500_KHZ: *bw_idx = 9; break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t sx12xx_airtime(const struct device *dev, uint32_t data_len)
|
||||
{
|
||||
uint32_t bw_idx;
|
||||
|
||||
if (sx12xx_get_bandwidth_idx(dev_data.tx_cfg.bandwidth, &bw_idx) < 0) {
|
||||
bw_idx = 0;
|
||||
}
|
||||
|
||||
return Radio.TimeOnAir(MODEM_LORA,
|
||||
bw_idx,
|
||||
dev_data.tx_cfg.datarate,
|
||||
dev_data.tx_cfg.coding_rate,
|
||||
dev_data.tx_cfg.preamble_len,
|
||||
0, data_len, !dev_data.tx_cfg.packet_crc_disable);
|
||||
}
|
||||
|
||||
int sx12xx_lora_send(const struct device *dev, uint8_t *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
|
||||
struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
|
||||
K_POLL_TYPE_SIGNAL,
|
||||
K_POLL_MODE_NOTIFY_ONLY,
|
||||
&done);
|
||||
uint32_t air_time;
|
||||
int ret;
|
||||
|
||||
/* Validate that we have a TX configuration */
|
||||
if (!dev_data.tx_cfg.frequency) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = sx12xx_lora_send_async(dev, data, data_len, &done);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Calculate expected airtime of the packet */
|
||||
air_time = sx12xx_airtime(dev, data_len);
|
||||
LOG_DBG("Expected air time of %u bytes = %u ms", data_len, air_time);
|
||||
|
||||
/* Wait for the packet to finish transmitting.
|
||||
* Use twice the tx duration to ensure that we are actually detecting
|
||||
* a failed transmission, and not some minor timing variation between
|
||||
* modem and driver.
|
||||
*/
|
||||
ret = k_poll(&evt, 1, K_MSEC(2 * air_time));
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Packet transmission failed!");
|
||||
if (!modem_release(&dev_data)) {
|
||||
/* TX done interrupt is currently running */
|
||||
k_poll(&evt, 1, K_FOREVER);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sx12xx_lora_send_async(const struct device *dev, uint8_t *data,
|
||||
uint32_t data_len, struct k_poll_signal *async)
|
||||
{
|
||||
/* Ensure available, freed by sx12xx_ev_tx_done */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Store signal */
|
||||
dev_data.operation_done = async;
|
||||
|
||||
Radio.SetMaxPayloadLength(MODEM_LORA, data_len);
|
||||
|
||||
Radio.Send(data, data_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
|
||||
k_timeout_t timeout, int16_t *rssi, int8_t *snr)
|
||||
{
|
||||
struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
|
||||
struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
|
||||
K_POLL_TYPE_SIGNAL,
|
||||
K_POLL_MODE_NOTIFY_ONLY,
|
||||
&done);
|
||||
int ret;
|
||||
|
||||
/* Ensure available, decremented by sx12xx_ev_rx_done or on timeout */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dev_data.async_rx_cb = NULL;
|
||||
/* Store operation signal */
|
||||
dev_data.operation_done = &done;
|
||||
/* Set data output location */
|
||||
dev_data.rx_params.buf = data;
|
||||
dev_data.rx_params.size = &size;
|
||||
dev_data.rx_params.rssi = rssi;
|
||||
dev_data.rx_params.snr = snr;
|
||||
|
||||
Radio.SetMaxPayloadLength(MODEM_LORA, 255);
|
||||
Radio.Rx(0);
|
||||
|
||||
ret = k_poll(&evt, 1, timeout);
|
||||
if (ret < 0) {
|
||||
if (!modem_release(&dev_data)) {
|
||||
/* Releasing the modem failed, which means that
|
||||
* the RX callback is currently running. Wait until
|
||||
* the RX callback finishes and we get our packet.
|
||||
*/
|
||||
k_poll(&evt, 1, K_FOREVER);
|
||||
|
||||
/* We did receive a packet */
|
||||
return size;
|
||||
}
|
||||
LOG_INF("Receive timeout");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (done.result < 0) {
|
||||
LOG_ERR("Receive error");
|
||||
return done.result;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int sx12xx_lora_recv_async(const struct device *dev, lora_recv_cb cb, void *user_data)
|
||||
{
|
||||
/* Cancel ongoing reception */
|
||||
if (cb == NULL) {
|
||||
if (!modem_release(&dev_data)) {
|
||||
/* Not receiving or already being stopped */
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ensure available */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Store parameters */
|
||||
dev_data.async_rx_cb = cb;
|
||||
dev_data.async_user_data = user_data;
|
||||
|
||||
/* Start reception */
|
||||
Radio.SetMaxPayloadLength(MODEM_LORA, 255);
|
||||
Radio.Rx(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check if only the TX/RX direction changed (all radio params identical). */
|
||||
static bool sx12xx_only_direction_changed(const struct lora_modem_config *a,
|
||||
const struct lora_modem_config *b)
|
||||
{
|
||||
return a->frequency == b->frequency &&
|
||||
a->bandwidth == b->bandwidth &&
|
||||
a->datarate == b->datarate &&
|
||||
a->coding_rate == b->coding_rate &&
|
||||
a->preamble_len == b->preamble_len &&
|
||||
a->tx_power == b->tx_power &&
|
||||
a->iq_inverted == b->iq_inverted &&
|
||||
a->public_network == b->public_network &&
|
||||
a->tx != b->tx;
|
||||
}
|
||||
|
||||
int sx12xx_lora_config(const struct device *dev,
|
||||
struct lora_modem_config *config)
|
||||
{
|
||||
bool crc = !config->packet_crc_disable;
|
||||
uint32_t bw_idx;
|
||||
int ret;
|
||||
|
||||
ret = sx12xx_get_bandwidth_idx(config->bandwidth, &bw_idx);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Unsupported bandwidth: %d", config->bandwidth);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fast path: if only TX↔RX direction changed and the target direction
|
||||
* was already configured once with the same params, skip the full
|
||||
* RadioSetTxConfig/RadioSetRxConfig (saves ~35ms of SPI traffic).
|
||||
* RadioSetTxConfig MUST have been called at least once to set
|
||||
* TxTimeout=4000; RadioSetRxConfig MUST have been called at least
|
||||
* once to set PayloadLength/SymbTimeout/IQ polarity workaround. */
|
||||
if (dev_data.last_cfg_valid &&
|
||||
sx12xx_only_direction_changed(config, &dev_data.last_cfg)) {
|
||||
if (config->tx && dev_data.tx_configured) {
|
||||
LOG_DBG("lora_config: fast TX switch (skip full reconfig)");
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
memcpy(&dev_data.tx_cfg, config, sizeof(dev_data.tx_cfg));
|
||||
dev_data.last_cfg = *config;
|
||||
modem_release(&dev_data);
|
||||
return 0;
|
||||
}
|
||||
if (!config->tx && dev_data.rx_configured) {
|
||||
LOG_DBG("lora_config: fast RX switch (skip full reconfig)");
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
dev_data.last_cfg = *config;
|
||||
modem_release(&dev_data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INF("lora_config: bw_enum=%d bw_idx=%u tx=%d freq=%u sf=%d",
|
||||
config->bandwidth, bw_idx, config->tx, config->frequency,
|
||||
config->datarate);
|
||||
|
||||
/* Ensure available, decremented after configuration */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
Radio.SetChannel(config->frequency);
|
||||
|
||||
if (config->tx) {
|
||||
/* Store TX config locally for airtime calculations */
|
||||
memcpy(&dev_data.tx_cfg, config, sizeof(dev_data.tx_cfg));
|
||||
/* Configure radio driver */
|
||||
Radio.SetTxConfig(MODEM_LORA, config->tx_power, 0,
|
||||
bw_idx, config->datarate,
|
||||
config->coding_rate, config->preamble_len,
|
||||
false, crc, 0, 0, config->iq_inverted, 4000);
|
||||
dev_data.tx_configured = true;
|
||||
} else {
|
||||
/* TODO: Get symbol timeout value from config parameters */
|
||||
Radio.SetRxConfig(MODEM_LORA, bw_idx,
|
||||
config->datarate, config->coding_rate,
|
||||
0, config->preamble_len, 10, false, 0,
|
||||
crc, false, 0, config->iq_inverted, true);
|
||||
dev_data.rx_configured = true;
|
||||
}
|
||||
|
||||
Radio.SetPublicNetwork(config->public_network);
|
||||
|
||||
dev_data.last_cfg = *config;
|
||||
dev_data.last_cfg_valid = true;
|
||||
|
||||
modem_release(&dev_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sx12xx_lora_test_cw(const struct device *dev, uint32_t frequency,
|
||||
int8_t tx_power,
|
||||
uint16_t duration)
|
||||
{
|
||||
/* Ensure available, freed in sx12xx_ev_tx_done */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
Radio.SetTxContinuousWave(frequency, tx_power, duration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sx12xx_init(const struct device *dev)
|
||||
{
|
||||
atomic_set(&dev_data.modem_usage, 0);
|
||||
|
||||
dev_data.dev = dev;
|
||||
dev_data.events.TxDone = sx12xx_ev_tx_done;
|
||||
dev_data.events.RxDone = sx12xx_ev_rx_done;
|
||||
dev_data.events.RxError = sx12xx_ev_rx_error;
|
||||
/* TX timeout event raises at the end of the test CW transmission */
|
||||
dev_data.events.TxTimeout = sx12xx_ev_tx_timed_out;
|
||||
Radio.Init(&dev_data.events);
|
||||
|
||||
/*
|
||||
* Automatically place the radio into sleep mode upon boot.
|
||||
* The required `lora_config` call before transmission or reception
|
||||
* will bring the radio out of sleep mode before it is used. The radio
|
||||
* is automatically placed back into sleep mode upon TX or RX
|
||||
* completion.
|
||||
*/
|
||||
Radio.Sleep();
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,346 +0,0 @@
|
||||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from west.commands import WestCommand
|
||||
|
||||
from zephyr_ext_common import ZEPHYR_BASE
|
||||
|
||||
sys.path.append(os.fspath(Path(__file__).parent.parent))
|
||||
import zephyr_module
|
||||
|
||||
|
||||
class Blobs(WestCommand):
|
||||
DEFAULT_LIST_FMT = '{module} {status} {path} {type} {abspath}'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'blobs',
|
||||
# Keep this in sync with the string in west-commands.yml.
|
||||
'work with binary blobs',
|
||||
'Work with binary blobs',
|
||||
accepts_unknown_args=False,
|
||||
)
|
||||
|
||||
def do_add_parser(self, parser_adder):
|
||||
parser = parser_adder.add_parser(
|
||||
self.name,
|
||||
help=self.help,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=self.description,
|
||||
epilog=textwrap.dedent(f'''\
|
||||
FORMAT STRINGS
|
||||
--------------
|
||||
|
||||
Blobs are listed using a Python 3 format string. Arguments
|
||||
to the format string are accessed by name.
|
||||
|
||||
The default format string is:
|
||||
|
||||
"{self.DEFAULT_LIST_FMT}"
|
||||
|
||||
The following arguments are available:
|
||||
|
||||
- module: name of the module that contains this blob
|
||||
- abspath: blob absolute path
|
||||
- status: short status (A: present, M: hash failure, D: not present)
|
||||
- path: blob local path from <module>/zephyr/blobs/
|
||||
- sha256: blob SHA256 hash in hex
|
||||
- type: type of blob
|
||||
- version: version string
|
||||
- license_path: path to the license file for the blob
|
||||
- license-abspath: absolute path to the license file for the blob
|
||||
- click-through: need license click-through or not
|
||||
- uri: URI to the remote location of the blob
|
||||
- description: blob text description
|
||||
- doc-url: URL to the documentation for this blob
|
||||
'''),
|
||||
)
|
||||
|
||||
# Remember to update west-completion.bash if you add or remove
|
||||
# flags
|
||||
parser.add_argument(
|
||||
'subcmd', nargs=1, choices=['list', 'fetch', 'clean'], help='sub-command to execute'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'modules',
|
||||
metavar='MODULE',
|
||||
nargs='*',
|
||||
help='''zephyr modules to operate on;
|
||||
all modules will be used if not given''',
|
||||
)
|
||||
|
||||
group = parser.add_argument_group('west blob list options')
|
||||
group.add_argument(
|
||||
'-f',
|
||||
'--format',
|
||||
help='''format string to use to list each blob;
|
||||
see FORMAT STRINGS below''',
|
||||
)
|
||||
|
||||
group = parser.add_argument_group('west blobs fetch options')
|
||||
group.add_argument(
|
||||
'-l',
|
||||
'--allow-regex',
|
||||
help='''Regex pattern to apply to the blob local path.
|
||||
Only local paths matching this regex will be fetched.
|
||||
Note that local paths are relative to the module directory''',
|
||||
)
|
||||
group.add_argument(
|
||||
'-a',
|
||||
'--auto-accept',
|
||||
action='store_true',
|
||||
help='''auto accept license if the fetching needs click-through''',
|
||||
)
|
||||
group.add_argument(
|
||||
'--cache-dirs',
|
||||
help='''Semicolon-separated list of directories to search for cached
|
||||
blobs before downloading. Cache files may use the original
|
||||
filename or be suffixed with `.<sha256>`.''',
|
||||
)
|
||||
group.add_argument(
|
||||
'--auto-cache',
|
||||
help='''Path to a directory that is automatically populated when a blob
|
||||
is downloaded. Cached blobs are stored using the original
|
||||
filename suffixed with `.<sha256>`.''',
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def get_blobs(self, args):
|
||||
blobs = []
|
||||
modules = args.modules
|
||||
all_modules = zephyr_module.parse_modules(ZEPHYR_BASE, self.manifest)
|
||||
all_names = [m.meta.get('name', None) for m in all_modules]
|
||||
|
||||
unknown = set(modules) - set(all_names)
|
||||
|
||||
if len(unknown):
|
||||
self.die(f'Unknown module(s): {unknown}')
|
||||
|
||||
for module in all_modules:
|
||||
# Filter by module
|
||||
module_name = module.meta.get('name', None)
|
||||
if len(modules) and module_name not in modules:
|
||||
continue
|
||||
|
||||
blobs += zephyr_module.process_blobs(module.project, module.meta)
|
||||
|
||||
return blobs
|
||||
|
||||
def list(self, args):
|
||||
blobs = self.get_blobs(args)
|
||||
fmt = args.format or self.DEFAULT_LIST_FMT
|
||||
for blob in blobs:
|
||||
self.inf(fmt.format(**blob))
|
||||
|
||||
def ensure_folder(self, path):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def handle_auto_cache(self, blob, auto_cache_dir) -> Path:
|
||||
"""
|
||||
This function guarantees that a given blob exists in the auto-cache.
|
||||
It first checks whether the blob is already present. If so, it
|
||||
returns the path of this cached blob. If the blob is not yet cached,
|
||||
the blob is downloaded into the auto-cache directory and the path of
|
||||
the freshly cached blob is returned.
|
||||
"""
|
||||
cached_blob = self.get_cached_blob(blob, [auto_cache_dir])
|
||||
if cached_blob:
|
||||
return cached_blob
|
||||
name = Path(blob['path']).name
|
||||
sha256 = blob['sha256']
|
||||
self.download_blob(blob, auto_cache_dir / f'{name}.{sha256}')
|
||||
cached_blob = self.get_cached_blob(blob, [auto_cache_dir])
|
||||
assert cached_blob, f'Blob {name} still not cached in auto-cache.'
|
||||
return cached_blob
|
||||
|
||||
def get_cached_blob(self, blob, cache_dirs: list) -> Path | None:
|
||||
"""
|
||||
Look for a cached blob in the provided cache directories.
|
||||
A blob may be stored using either its original name or suffixed with
|
||||
its SHA256 hash (e.g. "<name>.<sha256>").
|
||||
Return the first matching path, or None if not found.
|
||||
"""
|
||||
name = Path(blob['path']).name
|
||||
sha256 = blob["sha256"]
|
||||
candidate_names = [
|
||||
f"{name}.{sha256}", # suffixed version
|
||||
name, # original blob name
|
||||
]
|
||||
|
||||
for cache_dir in cache_dirs:
|
||||
if not cache_dir.exists():
|
||||
continue
|
||||
for name in candidate_names:
|
||||
candidate_path = cache_dir / name
|
||||
if (
|
||||
zephyr_module.get_blob_status(candidate_path, sha256)
|
||||
== zephyr_module.BLOB_PRESENT
|
||||
):
|
||||
return candidate_path
|
||||
return None
|
||||
|
||||
def download_blob(self, blob, path):
|
||||
'''Download a blob from its url to a given path.'''
|
||||
url = blob['url']
|
||||
scheme = urlparse(url).scheme
|
||||
self.dbg(f'Fetching blob from url {url} with {scheme} to path: {path}')
|
||||
import fetchers
|
||||
|
||||
fetcher = fetchers.get_fetcher_cls(scheme)
|
||||
self.dbg(f'Found fetcher: {fetcher}')
|
||||
inst = fetcher()
|
||||
self.ensure_folder(path)
|
||||
inst.fetch(url, path)
|
||||
|
||||
def fetch_blob(self, args, blob):
|
||||
"""
|
||||
Ensures that the specified blob is available at its path.
|
||||
If caching is enabled and the blob exists in the cache, it is copied
|
||||
from there. Otherwise, the blob is downloaded from its URL and placed
|
||||
at the target path.
|
||||
"""
|
||||
path = Path(blob['abspath'])
|
||||
|
||||
# collect existing cache dirs specified as args, otherwise from west config
|
||||
cache_dirs = args.cache_dirs
|
||||
auto_cache_dir = args.auto_cache
|
||||
if self.has_config:
|
||||
if cache_dirs is None:
|
||||
cache_dirs = self.config.get('blobs.cache-dirs')
|
||||
if auto_cache_dir is None:
|
||||
auto_cache_dir = self.config.get('blobs.auto-cache')
|
||||
|
||||
# expand user home for each cache directory
|
||||
if auto_cache_dir is not None:
|
||||
auto_cache_dir = Path(auto_cache_dir).expanduser()
|
||||
if cache_dirs is not None:
|
||||
cache_dirs = [Path(p).expanduser() for p in cache_dirs.split(';') if p]
|
||||
|
||||
# search for cached blob in the cache directories
|
||||
cached_blob = self.get_cached_blob(blob, cache_dirs or [])
|
||||
|
||||
# If blob is not found in cache directories: Use auto-cache if enabled
|
||||
if not cached_blob and auto_cache_dir:
|
||||
cached_blob = self.handle_auto_cache(blob, auto_cache_dir)
|
||||
|
||||
# Copy blob if it is cached, otherwise download it
|
||||
if cached_blob:
|
||||
self.dbg(f'Copy cached blob: {cached_blob}')
|
||||
self.ensure_folder(path)
|
||||
shutil.copy(cached_blob, path)
|
||||
else:
|
||||
self.download_blob(blob, path)
|
||||
|
||||
# Compare the checksum of a file we've just downloaded
|
||||
# to the digest in blob metadata, warn user if they differ.
|
||||
def verify_blob(self, blob) -> bool:
|
||||
self.dbg(f"Verifying blob {blob['module']}: {blob['abspath']}")
|
||||
|
||||
status = zephyr_module.get_blob_status(blob['abspath'], blob['sha256'])
|
||||
if status == zephyr_module.BLOB_OUTDATED:
|
||||
self.err(
|
||||
textwrap.dedent(
|
||||
f'''\
|
||||
The checksum of the downloaded file does not match that
|
||||
in the blob metadata:
|
||||
- if it is not certain that the download was successful,
|
||||
try running 'west blobs fetch {blob['module']}'
|
||||
to re-download the file
|
||||
- if the error persists, please consider contacting
|
||||
the maintainers of the module so that they can check
|
||||
the corresponding blob metadata
|
||||
|
||||
Module: {blob['module']}
|
||||
Blob: {blob['path']}
|
||||
URL: {blob['url']}
|
||||
Info: {blob['description']}'''
|
||||
)
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
def fetch(self, args):
|
||||
bad_checksum_count = 0
|
||||
blobs = self.get_blobs(args)
|
||||
for blob in blobs:
|
||||
if blob['status'] == zephyr_module.BLOB_PRESENT:
|
||||
self.dbg(f"Blob {blob['module']}: {blob['abspath']} is up to date")
|
||||
continue
|
||||
|
||||
# if args.allow_regex is set, use it to filter the blob by path
|
||||
if args.allow_regex and not re.match(args.allow_regex, blob['path']):
|
||||
self.dbg(
|
||||
f"Blob {blob['module']}: {blob['abspath']} does not match regex "
|
||||
f"'{args.allow_regex}', skipping"
|
||||
)
|
||||
continue
|
||||
self.inf(f"Fetching blob {blob['module']}: {blob['abspath']}")
|
||||
|
||||
if blob.get('click-through') and not args.auto_accept:
|
||||
while True:
|
||||
user_input = input(
|
||||
"For this blob, need to read and accept "
|
||||
"license to continue. Read it?\n"
|
||||
"Please type 'y' or 'n' and press enter to confirm: "
|
||||
)
|
||||
if user_input.upper() == "Y" or user_input.upper() == "N":
|
||||
break
|
||||
|
||||
if user_input.upper() != "Y":
|
||||
self.wrn('Skip fetching this blob.')
|
||||
continue
|
||||
|
||||
with open(blob['license-abspath'], encoding="utf-8") as license_file:
|
||||
license_content = license_file.read()
|
||||
print(license_content)
|
||||
|
||||
while True:
|
||||
user_input = input(
|
||||
"Accept license to continue?\n"
|
||||
"Please type 'y' or 'n' and press enter to confirm: "
|
||||
)
|
||||
if user_input.upper() == "Y" or user_input.upper() == "N":
|
||||
break
|
||||
|
||||
if user_input.upper() != "Y":
|
||||
self.wrn('Skip fetching this blob.')
|
||||
continue
|
||||
|
||||
self.fetch_blob(args, blob)
|
||||
if not self.verify_blob(blob):
|
||||
bad_checksum_count += 1
|
||||
|
||||
if bad_checksum_count:
|
||||
self.err(f"{bad_checksum_count} blobs have bad checksums")
|
||||
sys.exit(os.EX_DATAERR)
|
||||
|
||||
def clean(self, args):
|
||||
blobs = self.get_blobs(args)
|
||||
for blob in blobs:
|
||||
if blob['status'] == zephyr_module.BLOB_NOT_PRESENT:
|
||||
self.dbg(f"Blob {blob['module']}: {blob['abspath']} not in filesystem")
|
||||
continue
|
||||
self.inf(f"Deleting blob {blob['module']}: {blob['status']} {blob['abspath']}")
|
||||
blob['abspath'].unlink()
|
||||
|
||||
def do_run(self, args, _):
|
||||
self.dbg(f"subcmd: '{args.subcmd[0]}' modules: {args.modules}")
|
||||
|
||||
subcmd = getattr(self, args.subcmd[0])
|
||||
|
||||
if args.subcmd[0] != 'list' and args.format is not None:
|
||||
self.die('unexpected --format argument; this is a "west blobs list" option')
|
||||
|
||||
subcmd(args)
|
||||
Reference in New Issue
Block a user