lorawan: services: add Fragmented Data Block Transport
This service receives fragmented data (usually firmware images) and stores them in the slot1_partition in the flash. Also update CMakeLists.txt in loramac-node module to be able to use FragDecoder.c Signed-off-by: Martin Jäger <martin@libre.solar>
This commit is contained in:
parent
e85074329e
commit
f3e41c6a25
7 changed files with 622 additions and 0 deletions
|
|
@ -373,6 +373,24 @@ int lorawan_clock_sync_get(uint32_t *gps_time);
|
||||||
|
|
||||||
#endif /* CONFIG_LORAWAN_APP_CLOCK_SYNC */
|
#endif /* CONFIG_LORAWAN_APP_CLOCK_SYNC */
|
||||||
|
|
||||||
|
#ifdef CONFIG_LORAWAN_FRAG_TRANSPORT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run Fragmented Data Block Transport service
|
||||||
|
*
|
||||||
|
* This service receives fragmented data (usually firmware images) and
|
||||||
|
* stores them in the image-1 flash partition.
|
||||||
|
*
|
||||||
|
* After all fragments have been received, the provided callback is invoked.
|
||||||
|
*
|
||||||
|
* @param transport_finished_cb Callback for notification of finished data transfer.
|
||||||
|
*
|
||||||
|
* @return 0 if successful, negative errno otherwise.
|
||||||
|
*/
|
||||||
|
int lorawan_frag_transport_run(void (*transport_finished_cb)(void));
|
||||||
|
|
||||||
|
#endif /* CONFIG_LORAWAN_FRAG_TRANSPORT */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ if(${CONFIG_HAS_SEMTECH_LORAMAC})
|
||||||
zephyr_library_include_directories(
|
zephyr_library_include_directories(
|
||||||
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac
|
||||||
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region
|
||||||
|
# required for FUOTA FragDecoder.h
|
||||||
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/apps/LoRaMac/common/LmHandler/packages
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -53,6 +55,10 @@ zephyr_library_sources_ifdef(CONFIG_HAS_SEMTECH_LORAMAC
|
||||||
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/LoRaMacSerializer.c
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/LoRaMacSerializer.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_LORAWAN_FRAG_TRANSPORT
|
||||||
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/apps/LoRaMac/common/LmHandler/packages/FragDecoder.c
|
||||||
|
)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(CONFIG_HAS_SEMTECH_LORAMAC
|
zephyr_library_sources_ifdef(CONFIG_HAS_SEMTECH_LORAMAC
|
||||||
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region/Region.c
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region/Region.c
|
||||||
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region/RegionCommon.c
|
${ZEPHYR_LORAMAC_NODE_MODULE_DIR}/src/mac/region/RegionCommon.c
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,12 @@ if(CONFIG_LORAWAN_SERVICES)
|
||||||
clock_sync.c
|
clock_sync.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(
|
||||||
|
CONFIG_LORAWAN_FRAG_TRANSPORT
|
||||||
|
frag_flash.c
|
||||||
|
frag_transport.c
|
||||||
|
)
|
||||||
|
|
||||||
zephyr_library_sources_ifdef(
|
zephyr_library_sources_ifdef(
|
||||||
CONFIG_LORAWAN_REMOTE_MULTICAST
|
CONFIG_LORAWAN_REMOTE_MULTICAST
|
||||||
multicast.c
|
multicast.c
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,90 @@ config LORAWAN_APP_CLOCK_SYNC_PERIODICITY
|
||||||
|
|
||||||
Default setting: 24h.
|
Default setting: 24h.
|
||||||
|
|
||||||
|
config LORAWAN_FRAG_TRANSPORT
|
||||||
|
bool "Fragmented Data Block Transport"
|
||||||
|
select FLASH_MAP
|
||||||
|
select FLASH_PAGE_LAYOUT
|
||||||
|
select IMG_MANAGER
|
||||||
|
help
|
||||||
|
Enables the LoRaWAN Fragmented Data Block Transport service
|
||||||
|
according to TS004-1.0.0 as published by the LoRa Alliance.
|
||||||
|
|
||||||
|
The used default port for this service is 201.
|
||||||
|
|
||||||
|
DT_CHOSEN_Z_CODE_PARTITION := zephyr,code-partition
|
||||||
|
|
||||||
|
config LORAWAN_FRAG_TRANSPORT_IMAGE_SIZE
|
||||||
|
int "Total size of firmware image"
|
||||||
|
depends on LORAWAN_FRAG_TRANSPORT
|
||||||
|
default $(dt_chosen_reg_size_int,$(DT_CHOSEN_Z_CODE_PARTITION))
|
||||||
|
help
|
||||||
|
Size of the flash partition for the application firmware image
|
||||||
|
in bytes.
|
||||||
|
|
||||||
|
The minimum number of fragments to be transferred is calculated from
|
||||||
|
this value divided by the fragment size.
|
||||||
|
|
||||||
|
This setting has significant influence on RAM usage.
|
||||||
|
|
||||||
|
config LORAWAN_FRAG_TRANSPORT_MAX_FRAG_SIZE
|
||||||
|
int "Maximum size of transported fragments"
|
||||||
|
depends on LORAWAN_FRAG_TRANSPORT
|
||||||
|
range 1 239
|
||||||
|
default 232
|
||||||
|
help
|
||||||
|
Maximum size of one fragment transferred during the fragmented data
|
||||||
|
block transport session of the FUOTA process. It is chosen on the
|
||||||
|
server side.
|
||||||
|
|
||||||
|
The fragment has to fit into the LoRaWAN payload, which can be up to
|
||||||
|
242 bytes depending on the region and frequency settings. 3 bytes of
|
||||||
|
the payload are consumed for protocol information.
|
||||||
|
|
||||||
|
For some MCUs like the STM32WL the fragment size has to be a multiple
|
||||||
|
of 8 (see flash driver for further information).
|
||||||
|
|
||||||
|
This setting has significant influence on RAM usage. If the exact
|
||||||
|
fragment size is known, use that value for MIN and MAX config to
|
||||||
|
reduce memory consumption.
|
||||||
|
|
||||||
|
config LORAWAN_FRAG_TRANSPORT_MIN_FRAG_SIZE
|
||||||
|
int "Minimum size of transported fragments"
|
||||||
|
depends on LORAWAN_FRAG_TRANSPORT
|
||||||
|
range 1 239
|
||||||
|
default 48
|
||||||
|
help
|
||||||
|
Minimum size of one fragment transferred during the fragmented data
|
||||||
|
block transport session of the FUOTA process. It is chosen on the
|
||||||
|
server side.
|
||||||
|
|
||||||
|
The fragment has to fit into the LoRaWAN payload, which can be up to
|
||||||
|
242 bytes depending on the region and frequency settings. 3 bytes of
|
||||||
|
the payload are consumed for protocol information.
|
||||||
|
|
||||||
|
For some MCUs like the STM32WL the fragment size has to be a multiple
|
||||||
|
of 8 (see flash driver for further information).
|
||||||
|
|
||||||
|
This setting has significant influence on RAM usage. If the exact
|
||||||
|
fragment size is known, use that value for MIN and MAX config to
|
||||||
|
reduce memory consumption.
|
||||||
|
|
||||||
|
config LORAWAN_FRAG_TRANSPORT_MAX_REDUNDANCY
|
||||||
|
int "Percentage of redundant fragments"
|
||||||
|
depends on LORAWAN_FRAG_TRANSPORT
|
||||||
|
range 1 100
|
||||||
|
default 20
|
||||||
|
help
|
||||||
|
The built-in forward error correction (FEC) mechanism allows to
|
||||||
|
reconstruct missed packages from additional redundant packages
|
||||||
|
sent by the server after all packages have been sent.
|
||||||
|
|
||||||
|
This parameter specifies the maximum amount of packet loss (in
|
||||||
|
percent) for which it should be possible to reconstruct the full
|
||||||
|
firmware image.
|
||||||
|
|
||||||
|
This setting has significant influence on RAM usage.
|
||||||
|
|
||||||
config LORAWAN_REMOTE_MULTICAST
|
config LORAWAN_REMOTE_MULTICAST
|
||||||
bool "Remote Multicast Setup"
|
bool "Remote Multicast Setup"
|
||||||
depends on LORAWAN_APP_CLOCK_SYNC
|
depends on LORAWAN_APP_CLOCK_SYNC
|
||||||
|
|
|
||||||
129
subsys/lorawan/services/frag_flash.c
Normal file
129
subsys/lorawan/services/frag_flash.c
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2024 Libre Solar Technologies GmbH
|
||||||
|
* Copyright (c) 2022-2024 tado GmbH
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "frag_flash.h"
|
||||||
|
|
||||||
|
#include <FragDecoder.h>
|
||||||
|
|
||||||
|
#include <zephyr/dfu/mcuboot.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/storage/flash_map.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(lorawan_frag_flash, CONFIG_LORAWAN_SERVICES_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define TARGET_IMAGE_AREA FIXED_PARTITION_ID(slot1_partition)
|
||||||
|
|
||||||
|
struct frag_cache_entry {
|
||||||
|
uint32_t addr;
|
||||||
|
uint8_t data[FRAG_MAX_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct frag_cache_entry frag_cache[FRAG_MAX_REDUNDANCY];
|
||||||
|
static uint32_t frag_size;
|
||||||
|
static int cached_frags;
|
||||||
|
static bool use_cache;
|
||||||
|
|
||||||
|
static const struct flash_area *fa;
|
||||||
|
|
||||||
|
int frag_flash_init(uint32_t fragment_size)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (fragment_size > FRAG_MAX_SIZE) {
|
||||||
|
return -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
frag_size = fragment_size;
|
||||||
|
cached_frags = 0;
|
||||||
|
use_cache = false;
|
||||||
|
|
||||||
|
err = flash_area_open(TARGET_IMAGE_AREA, &fa);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Starting to erase flash area");
|
||||||
|
|
||||||
|
err = flash_area_erase(fa, 0, fa->fa_size);
|
||||||
|
|
||||||
|
LOG_DBG("Finished erasing flash area");
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t frag_flash_write(uint32_t addr, uint8_t *data, uint32_t size)
|
||||||
|
{
|
||||||
|
int8_t err = 0;
|
||||||
|
|
||||||
|
if (!use_cache) {
|
||||||
|
LOG_DBG("Writing %u bytes to addr 0x%X", size, addr);
|
||||||
|
|
||||||
|
err = flash_area_write(fa, addr, data, size);
|
||||||
|
} else {
|
||||||
|
LOG_DBG("Caching %u bytes for addr 0x%X", size, addr);
|
||||||
|
|
||||||
|
if (size != frag_size) {
|
||||||
|
LOG_ERR("Invalid fragment size %d", size);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overwrite fragment in cache if existing */
|
||||||
|
for (int i = 0; i < cached_frags; i++) {
|
||||||
|
if (frag_cache[i].addr == addr) {
|
||||||
|
memcpy(frag_cache[i].data, data, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise create new cache entry */
|
||||||
|
if (cached_frags < ARRAY_SIZE(frag_cache)) {
|
||||||
|
frag_cache[cached_frags].addr = addr;
|
||||||
|
memcpy(frag_cache[cached_frags].data, data, size);
|
||||||
|
cached_frags++;
|
||||||
|
} else {
|
||||||
|
LOG_ERR("Fragment cache too small");
|
||||||
|
err = -ENOSPC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t frag_flash_read(uint32_t addr, uint8_t *data, uint32_t size)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cached_frags; i++) {
|
||||||
|
if (frag_cache[i].addr == addr) {
|
||||||
|
memcpy(data, frag_cache[i].data, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flash_area_read(fa, addr, data, size) == 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frag_flash_use_cache(void)
|
||||||
|
{
|
||||||
|
use_cache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void frag_flash_finish(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
for (int i = 0; i < cached_frags; i++) {
|
||||||
|
LOG_DBG("Writing %u bytes to addr 0x%x", frag_size, frag_cache[i].addr);
|
||||||
|
flash_area_write(fa, frag_cache[i].addr, frag_cache[i].data, frag_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
flash_area_close(fa);
|
||||||
|
|
||||||
|
err = boot_request_upgrade(BOOT_UPGRADE_TEST);
|
||||||
|
if (err) {
|
||||||
|
LOG_ERR("Failed to request upgrade (err %d)", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
69
subsys/lorawan/services/frag_flash.h
Normal file
69
subsys/lorawan/services/frag_flash.h
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2024 Libre Solar Technologies GmbH
|
||||||
|
* Copyright (c) 2022-2024 tado GmbH
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_SUBSYS_LORAWAN_SERVICES_FRAG_FLASH_H_
|
||||||
|
#define ZEPHYR_SUBSYS_LORAWAN_SERVICES_FRAG_FLASH_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize flash driver and prepare partition for new firmware image.
|
||||||
|
*
|
||||||
|
* This function mass-erases the flash partition and may take a while to return.
|
||||||
|
*
|
||||||
|
* @param frag_size Fragment size used for this session
|
||||||
|
*
|
||||||
|
* @returns 0 for success, otherwise negative error code
|
||||||
|
*/
|
||||||
|
int frag_flash_init(uint32_t frag_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write received data fragment to flash.
|
||||||
|
*
|
||||||
|
* This function is called by FragDecoder from LoRaMAC-node stack.
|
||||||
|
*
|
||||||
|
* @param addr Flash address relative to start of slot
|
||||||
|
* @param data Data buffer
|
||||||
|
* @param size Number of bytes in the buffer
|
||||||
|
*
|
||||||
|
* @returns 0 for success, otherwise negative error code
|
||||||
|
*/
|
||||||
|
int8_t frag_flash_write(uint32_t addr, uint8_t *data, uint32_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read back data from flash.
|
||||||
|
*
|
||||||
|
* This function is called by FragDecoder from LoRaMAC-node stack.
|
||||||
|
*
|
||||||
|
* @param addr Flash address relative to start of slot
|
||||||
|
* @param data Data buffer
|
||||||
|
* @param size Number of bytes in the buffer
|
||||||
|
*
|
||||||
|
* @returns 0 for success, otherwise negative error code
|
||||||
|
*/
|
||||||
|
int8_t frag_flash_read(uint32_t addr, uint8_t *data, uint32_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start caching fragments in RAM.
|
||||||
|
*
|
||||||
|
* Coded/redundant fragments may be overwritten with future fragments,
|
||||||
|
* so we have to cache them in RAM instead of flash.
|
||||||
|
*
|
||||||
|
* This function must be called once all uncoded fragments have been received.
|
||||||
|
*/
|
||||||
|
void frag_flash_use_cache(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize flashing after sufficient fragments have been received.
|
||||||
|
*
|
||||||
|
* This call will also write cached fragments to flash.
|
||||||
|
*
|
||||||
|
* After this call the new firmware is ready to be checked and booted.
|
||||||
|
*/
|
||||||
|
void frag_flash_finish(void);
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_SUBSYS_LORAWAN_SERVICES_FRAG_FLASH_H_ */
|
||||||
310
subsys/lorawan/services/frag_transport.c
Normal file
310
subsys/lorawan/services/frag_transport.c
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022-2024 Libre Solar Technologies GmbH
|
||||||
|
* Copyright (c) 2022-2024 tado GmbH
|
||||||
|
*
|
||||||
|
* Parts of this implementation were inspired by LmhpFragmentation.c from the
|
||||||
|
* LoRaMac-node firmware repository https://github.com/Lora-net/LoRaMac-node
|
||||||
|
* written by Miguel Luis (Semtech).
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "frag_flash.h"
|
||||||
|
#include "lorawan_services.h"
|
||||||
|
|
||||||
|
#include <LoRaMac.h>
|
||||||
|
#include <FragDecoder.h>
|
||||||
|
#include <zephyr/lorawan/lorawan.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/random/random.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(lorawan_frag_transport, CONFIG_LORAWAN_SERVICES_LOG_LEVEL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version of LoRaWAN Fragmented Data Block Transport Specification
|
||||||
|
*
|
||||||
|
* This implementation only supports TS004-1.0.0.
|
||||||
|
*/
|
||||||
|
#define FRAG_TRANSPORT_VERSION 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum expected number of frag transport commands per packet
|
||||||
|
*
|
||||||
|
* The standard states "A message MAY carry more than one command". Even though this was not
|
||||||
|
* observed during testing, space for up to 3 packages is reserved.
|
||||||
|
*/
|
||||||
|
#define FRAG_TRANSPORT_MAX_CMDS_PER_PACKAGE 3
|
||||||
|
|
||||||
|
/* maximum length of frag_transport answers */
|
||||||
|
#define FRAG_TRANSPORT_MAX_ANS_LEN 5
|
||||||
|
|
||||||
|
enum frag_transport_commands {
|
||||||
|
FRAG_TRANSPORT_CMD_PKG_VERSION = 0x00,
|
||||||
|
FRAG_TRANSPORT_CMD_FRAG_STATUS = 0x01,
|
||||||
|
FRAG_TRANSPORT_CMD_FRAG_SESSION_SETUP = 0x02,
|
||||||
|
FRAG_TRANSPORT_CMD_FRAG_SESSION_DELETE = 0x03,
|
||||||
|
FRAG_TRANSPORT_CMD_DATA_FRAGMENT = 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct frag_transport_context {
|
||||||
|
/** Stores if a session is active */
|
||||||
|
bool is_active;
|
||||||
|
union {
|
||||||
|
uint8_t frag_session;
|
||||||
|
struct {
|
||||||
|
/** Multicast groups allowed to input to this frag session */
|
||||||
|
uint8_t mc_group_bit_mask: 4;
|
||||||
|
/** Identifies this session */
|
||||||
|
uint8_t frag_index: 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** Number of fragments of the data block for this session, max. 2^14-1 */
|
||||||
|
uint16_t nb_frag;
|
||||||
|
/** Number of fragments received in this session (including coded, uncoded and repeated) */
|
||||||
|
uint16_t nb_frag_received;
|
||||||
|
/** Size of each fragment in octets */
|
||||||
|
uint8_t frag_size;
|
||||||
|
union {
|
||||||
|
uint8_t control;
|
||||||
|
struct {
|
||||||
|
/** Random delay for some responses between 0 and 2^(BlockAckDelay + 4) */
|
||||||
|
uint8_t block_ack_delay: 3;
|
||||||
|
/** Used fragmentation algorithm (0 for forward error correction) */
|
||||||
|
uint8_t frag_algo: 3;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** Padding in the last fragment if total size is not a multiple of frag_size */
|
||||||
|
uint8_t padding;
|
||||||
|
/** Application-specific descriptor for the data block, e.g. firmware version */
|
||||||
|
uint32_t descriptor;
|
||||||
|
|
||||||
|
/* variables required for FragDecoder.h */
|
||||||
|
FragDecoderCallbacks_t decoder_callbacks;
|
||||||
|
int32_t decoder_process_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The frag decoder is a singleton, so we can only have one ongoing frag session at a time, even
|
||||||
|
* though the standard allows up to 4 sessions
|
||||||
|
*/
|
||||||
|
static struct frag_transport_context ctx;
|
||||||
|
|
||||||
|
/* Callback for notification of finished firmware transfer */
|
||||||
|
static void (*finished_cb)(void);
|
||||||
|
|
||||||
|
static void frag_transport_package_callback(uint8_t port, bool data_pending, int16_t rssi,
|
||||||
|
int8_t snr, uint8_t len, const uint8_t *rx_buf)
|
||||||
|
{
|
||||||
|
uint8_t tx_buf[FRAG_TRANSPORT_MAX_CMDS_PER_PACKAGE * FRAG_TRANSPORT_MAX_ANS_LEN];
|
||||||
|
uint8_t tx_pos = 0;
|
||||||
|
uint8_t rx_pos = 0;
|
||||||
|
int ans_delay = 0;
|
||||||
|
|
||||||
|
__ASSERT(port == LORAWAN_PORT_FRAG_TRANSPORT, "Wrong port %d", port);
|
||||||
|
|
||||||
|
while (rx_pos < len) {
|
||||||
|
uint8_t command_id = rx_buf[rx_pos++];
|
||||||
|
|
||||||
|
if (sizeof(tx_buf) - tx_pos < FRAG_TRANSPORT_MAX_ANS_LEN) {
|
||||||
|
LOG_ERR("insufficient tx_buf size, some requests discarded");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command_id) {
|
||||||
|
case FRAG_TRANSPORT_CMD_PKG_VERSION:
|
||||||
|
tx_buf[tx_pos++] = FRAG_TRANSPORT_CMD_PKG_VERSION;
|
||||||
|
tx_buf[tx_pos++] = LORAWAN_PACKAGE_ID_FRAG_TRANSPORT_BLOCK;
|
||||||
|
tx_buf[tx_pos++] = FRAG_TRANSPORT_VERSION;
|
||||||
|
break;
|
||||||
|
case FRAG_TRANSPORT_CMD_FRAG_STATUS: {
|
||||||
|
uint8_t frag_status = rx_buf[rx_pos++] & 0x07;
|
||||||
|
uint8_t participants = frag_status & 0x01;
|
||||||
|
uint8_t index = frag_status >> 1;
|
||||||
|
|
||||||
|
LOG_DBG("FragSessionStatusReq index %d, participants: %u", index,
|
||||||
|
participants);
|
||||||
|
|
||||||
|
uint8_t missing_frag = CLAMP(ctx.nb_frag - ctx.nb_frag_received, 0, 255);
|
||||||
|
|
||||||
|
FragDecoderStatus_t decoder_status = FragDecoderGetStatus();
|
||||||
|
uint8_t memory_error = decoder_status.MatrixError;
|
||||||
|
|
||||||
|
if (participants == 1 || missing_frag > 0) {
|
||||||
|
tx_buf[tx_pos++] = FRAG_TRANSPORT_CMD_FRAG_STATUS;
|
||||||
|
tx_buf[tx_pos++] = ctx.nb_frag_received & 0xFF;
|
||||||
|
tx_buf[tx_pos++] =
|
||||||
|
(index << 6) | ((ctx.nb_frag_received >> 8) & 0x3F);
|
||||||
|
tx_buf[tx_pos++] = missing_frag;
|
||||||
|
tx_buf[tx_pos++] = memory_error & 0x01;
|
||||||
|
|
||||||
|
ans_delay = sys_rand32_get() % (1U << (ctx.block_ack_delay + 4));
|
||||||
|
|
||||||
|
LOG_DBG("FragSessionStatusAns index %d, NbFragReceived: %u, "
|
||||||
|
"MissingFrag: %u, MemoryError: %u, delay: %d",
|
||||||
|
index, ctx.nb_frag_received, missing_frag, memory_error,
|
||||||
|
ans_delay);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FRAG_TRANSPORT_CMD_FRAG_SESSION_SETUP: {
|
||||||
|
uint8_t frag_session = rx_buf[rx_pos++] & 0x3F;
|
||||||
|
uint8_t index = frag_session >> 4;
|
||||||
|
uint8_t status = index << 6;
|
||||||
|
|
||||||
|
if (!ctx.is_active || ctx.frag_index == index) {
|
||||||
|
ctx.frag_session = frag_session;
|
||||||
|
ctx.nb_frag_received = 0;
|
||||||
|
|
||||||
|
ctx.nb_frag = sys_get_le16(rx_buf + rx_pos);
|
||||||
|
rx_pos += sizeof(uint16_t);
|
||||||
|
|
||||||
|
ctx.frag_size = rx_buf[rx_pos++];
|
||||||
|
ctx.control = rx_buf[rx_pos++];
|
||||||
|
ctx.padding = rx_buf[rx_pos++];
|
||||||
|
|
||||||
|
ctx.descriptor = sys_get_le32(rx_buf + rx_pos);
|
||||||
|
rx_pos += sizeof(uint32_t);
|
||||||
|
|
||||||
|
LOG_INF("FragSessionSetupReq index %d, nb_frag: %u, frag_size: %u, "
|
||||||
|
"padding: %u, control: 0x%x, descriptor: 0x%.8x",
|
||||||
|
index, ctx.nb_frag, ctx.frag_size, ctx.padding, ctx.control,
|
||||||
|
ctx.descriptor);
|
||||||
|
} else {
|
||||||
|
/* FragIndex unsupported */
|
||||||
|
status |= BIT(2);
|
||||||
|
|
||||||
|
LOG_WRN("FragSessionSetupReq failed. Session %u still active",
|
||||||
|
ctx.frag_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.frag_algo > 0) {
|
||||||
|
/* FragAlgo unsupported */
|
||||||
|
status |= BIT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.nb_frag > FRAG_MAX_NB || ctx.frag_size > FRAG_MAX_SIZE ||
|
||||||
|
ctx.nb_frag * ctx.frag_size > FragDecoderGetMaxFileSize()) {
|
||||||
|
/* Not enough memory */
|
||||||
|
status |= BIT(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Descriptor not used: Ignore Wrong Descriptor error */
|
||||||
|
|
||||||
|
if ((status & 0x1F) == 0) {
|
||||||
|
/*
|
||||||
|
* Assign callbacks after initialization to prevent the FragDecoder
|
||||||
|
* from writing byte-wise 0xFF to the entire flash. Instead, erase
|
||||||
|
* flash properly with own implementation.
|
||||||
|
*/
|
||||||
|
ctx.decoder_callbacks.FragDecoderWrite = NULL;
|
||||||
|
ctx.decoder_callbacks.FragDecoderRead = NULL;
|
||||||
|
|
||||||
|
FragDecoderInit(ctx.nb_frag, ctx.frag_size, &ctx.decoder_callbacks);
|
||||||
|
|
||||||
|
ctx.decoder_callbacks.FragDecoderWrite = frag_flash_write;
|
||||||
|
ctx.decoder_callbacks.FragDecoderRead = frag_flash_read;
|
||||||
|
|
||||||
|
frag_flash_init(ctx.frag_size);
|
||||||
|
ctx.is_active = true;
|
||||||
|
ctx.decoder_process_status = FRAG_SESSION_ONGOING;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_buf[tx_pos++] = FRAG_TRANSPORT_CMD_FRAG_SESSION_SETUP;
|
||||||
|
tx_buf[tx_pos++] = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FRAG_TRANSPORT_CMD_FRAG_SESSION_DELETE: {
|
||||||
|
uint8_t index = rx_buf[rx_pos++] & 0x03;
|
||||||
|
uint8_t status = 0x00;
|
||||||
|
|
||||||
|
status |= index;
|
||||||
|
if (!ctx.is_active || ctx.frag_index != index) {
|
||||||
|
/* Session does not exist */
|
||||||
|
status |= BIT(2);
|
||||||
|
} else {
|
||||||
|
ctx.is_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tx_buf[tx_pos++] = FRAG_TRANSPORT_CMD_FRAG_SESSION_DELETE;
|
||||||
|
tx_buf[tx_pos++] = status;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FRAG_TRANSPORT_CMD_DATA_FRAGMENT: {
|
||||||
|
ctx.nb_frag_received++;
|
||||||
|
|
||||||
|
uint16_t frag_index_n = sys_get_le16(rx_buf + rx_pos);
|
||||||
|
|
||||||
|
rx_pos += 2;
|
||||||
|
|
||||||
|
uint16_t frag_counter = frag_index_n & 0x3FFF;
|
||||||
|
uint8_t index = (frag_index_n >> 14) & 0x03;
|
||||||
|
|
||||||
|
if (!ctx.is_active || index != ctx.frag_index) {
|
||||||
|
LOG_DBG("DataFragment received for inactive session %u", index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.decoder_process_status == FRAG_SESSION_ONGOING) {
|
||||||
|
if (frag_counter > ctx.nb_frag) {
|
||||||
|
/* Additional fragments have to be cached in RAM for
|
||||||
|
* recovery algorithm.
|
||||||
|
*/
|
||||||
|
frag_flash_use_cache();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.decoder_process_status = FragDecoderProcess(
|
||||||
|
frag_counter, (uint8_t *)&rx_buf[rx_pos]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("DataFragment %u of %u (%u lost), session: %u, decoder result: %d",
|
||||||
|
frag_counter, ctx.nb_frag, frag_counter - ctx.nb_frag_received,
|
||||||
|
index, ctx.decoder_process_status);
|
||||||
|
|
||||||
|
if (ctx.decoder_process_status >= 0) {
|
||||||
|
/* Positive status corresponds to number of lost (but recovered)
|
||||||
|
* fragments. Value >= 0 means the transport is done.
|
||||||
|
*/
|
||||||
|
frag_flash_finish();
|
||||||
|
|
||||||
|
LOG_INF("Frag Transport finished successfully");
|
||||||
|
|
||||||
|
if (finished_cb != NULL) {
|
||||||
|
finished_cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.is_active = false;
|
||||||
|
/* avoid processing further fragments */
|
||||||
|
ctx.decoder_process_status = FRAG_SESSION_NOT_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx_pos += ctx.frag_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tx_pos > 0) {
|
||||||
|
lorawan_services_schedule_uplink(LORAWAN_PORT_FRAG_TRANSPORT, tx_buf, tx_pos,
|
||||||
|
ans_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct lorawan_downlink_cb downlink_cb = {
|
||||||
|
.port = (uint8_t)LORAWAN_PORT_FRAG_TRANSPORT,
|
||||||
|
.cb = frag_transport_package_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
int lorawan_frag_transport_run(void (*transport_finished_cb)(void))
|
||||||
|
{
|
||||||
|
finished_cb = transport_finished_cb;
|
||||||
|
|
||||||
|
/* initialize non-zero static variables */
|
||||||
|
ctx.decoder_process_status = FRAG_SESSION_NOT_STARTED;
|
||||||
|
|
||||||
|
lorawan_register_downlink_callback(&downlink_cb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue