lorawan: Add initial support for LoRaWAN
Add initial support for LoRaWAN based on Semtech's loramac-node library. Current implementation only supports OTAA config and sending data to LoRaWAN server like ThingsNetwork. While at it, this commit also moves the "loramac-node" library definition from drivers/lora to subsys/lorawan. This is required because, subsys/lorawan gets processed before drivers/lora and that creates issue while building. Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
This commit is contained in:
parent
88e3ea02ff
commit
3ce8540f3a
|
@ -9,6 +9,12 @@ config HAS_SEMTECH_LORAMAC
|
|||
help
|
||||
This option enables the use of Semtech's LoRaMac stack
|
||||
|
||||
config HAS_SEMTECH_SOFT_SE
|
||||
bool "Semtech Secure Element software implementation"
|
||||
help
|
||||
This option enables the use of Semtech's Secure Element
|
||||
software implementation
|
||||
|
||||
config HAS_SEMTECH_RADIO_DRIVERS
|
||||
bool "Semtech LoRa Radio Drivers"
|
||||
help
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
add_subdirectory(debug)
|
||||
add_subdirectory(logging)
|
||||
add_subdirectory_ifdef(CONFIG_LORAWAN lorawan)
|
||||
add_subdirectory_ifdef(CONFIG_BT bluetooth)
|
||||
add_subdirectory_ifdef(CONFIG_CONSOLE_SUBSYS console)
|
||||
add_subdirectory_ifdef(CONFIG_SHELL shell)
|
||||
|
|
|
@ -27,6 +27,8 @@ source "subsys/jwt/Kconfig"
|
|||
|
||||
source "subsys/logging/Kconfig"
|
||||
|
||||
source "subsys/lorawan/Kconfig"
|
||||
|
||||
source "subsys/mgmt/Kconfig"
|
||||
|
||||
source "subsys/net/Kconfig"
|
||||
|
|
23
subsys/lorawan/CMakeLists.txt
Normal file
23
subsys/lorawan/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# lorawan.c depends on the include directories exposed by the loramac-node
|
||||
# library. Hence, if it exists then the source files are added to that otherwise
|
||||
# a library with same name is created.
|
||||
if(TARGET loramac-node)
|
||||
set(ZEPHYR_CURRENT_LIBRARY loramac-node)
|
||||
else()
|
||||
zephyr_library_named(loramac-node)
|
||||
endif()
|
||||
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_AS923 REGION_AS923)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_AU915 REGION_AU915)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_CN470 REGION_CN470)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_CN779 REGION_CN779)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_EU433 REGION_EU433)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_EU868 REGION_EU868)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_KR920 REGION_KR920)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_IN865 REGION_IN865)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_US915 REGION_US915)
|
||||
zephyr_compile_definitions_ifdef(CONFIG_LORAMAC_REGION_RU864 REGION_RU864)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c)
|
61
subsys/lorawan/Kconfig
Normal file
61
subsys/lorawan/Kconfig
Normal file
|
@ -0,0 +1,61 @@
|
|||
# LoRaWAN configuration options
|
||||
|
||||
# Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig LORAWAN
|
||||
bool "LoRaWAN support [EXPERIMENTAL]"
|
||||
depends on LORA
|
||||
select HAS_SEMTECH_LORAMAC
|
||||
select HAS_SEMTECH_SOFT_SE
|
||||
help
|
||||
This option enables LoRaWAN support.
|
||||
|
||||
if LORAWAN
|
||||
|
||||
module = LORAWAN
|
||||
module-str = lorawan
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
choice
|
||||
prompt "LoRaWAN Region Selection"
|
||||
default LORAMAC_REGION_UNKNOWN
|
||||
help
|
||||
Select the LoRaWAN region.
|
||||
|
||||
config LORAMAC_REGION_UNKNOWN
|
||||
bool "Unknown region"
|
||||
|
||||
config LORAMAC_REGION_AS923
|
||||
bool "Asia 923MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_AU915
|
||||
bool "Australia 915MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_CN470
|
||||
bool "China 470MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_CN779
|
||||
bool "China 779MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_EU433
|
||||
bool "Europe 433MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_EU868
|
||||
bool "Europe 868MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_KR920
|
||||
bool "South Korea 920MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_IN865
|
||||
bool "India 865MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_US915
|
||||
bool "North America 915MHz Frequency band"
|
||||
|
||||
config LORAMAC_REGION_RU864
|
||||
bool "Russia 864MHz Frequency band"
|
||||
|
||||
endchoice
|
||||
|
||||
endif # LORAWAN
|
547
subsys/lorawan/lorawan.c
Normal file
547
subsys/lorawan/lorawan.c
Normal file
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <init.h>
|
||||
#include <errno.h>
|
||||
#include <lorawan/lorawan.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include <LoRaMac.h>
|
||||
|
||||
BUILD_ASSERT(!IS_ENABLED(CONFIG_LORAMAC_REGION_UNKNOWN),
|
||||
"Unknown region specified for LoRaWAN in Kconfig");
|
||||
|
||||
#ifdef CONFIG_LORAMAC_REGION_AS923
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AS923
|
||||
#elif CONFIG_LORAMAC_REGION_AU915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_AU915
|
||||
#elif CONFIG_LORAMAC_REGION_CN470
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN470
|
||||
#elif CONFIG_LORAMAC_REGION_CN779
|
||||
#define LORAWAN_REGION LORAMAC_REGION_CN779
|
||||
#elif CONFIG_LORAMAC_REGION_EU433
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU433
|
||||
#elif CONFIG_LORAMAC_REGION_EU868
|
||||
#define LORAWAN_REGION LORAMAC_REGION_EU868
|
||||
#elif CONFIG_LORAMAC_REGION_KR920
|
||||
#define LORAWAN_REGION LORAMAC_REGION_KR920
|
||||
#elif CONFIG_LORAMAC_REGION_IN865
|
||||
#define LORAWAN_REGION LORAMAC_REGION_IN865
|
||||
#elif CONFIG_LORAMAC_REGION_US915
|
||||
#define LORAWAN_REGION LORAMAC_REGION_US915
|
||||
#elif CONFIG_LORAMAC_REGION_RU864
|
||||
#define LORAWAN_REGION LORAMAC_REGION_RU864
|
||||
#else
|
||||
#error "At least one LoRaWAN region should be selected"
|
||||
#endif
|
||||
|
||||
/* Use version 1.0.3.0 for ABP */
|
||||
#define LORAWAN_ABP_VERSION 0x01000300
|
||||
|
||||
#define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(lorawan);
|
||||
|
||||
K_SEM_DEFINE(lorawan_config_sem, 0, 1);
|
||||
K_SEM_DEFINE(lorawan_tx_sem, 0, 1);
|
||||
|
||||
K_MUTEX_DEFINE(lorawan_join_mutex);
|
||||
K_MUTEX_DEFINE(lorawan_send_mutex);
|
||||
|
||||
static volatile bool join_status = false;
|
||||
static volatile bool send_status = false;
|
||||
|
||||
static enum lorawan_datarate lorawan_datarate = LORAWAN_DR_0;
|
||||
static uint8_t lorawan_conf_msg_tries = 1;
|
||||
static bool lorawan_adr_enable;
|
||||
|
||||
const char *status2str(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case LORAMAC_STATUS_OK:
|
||||
return "OK";
|
||||
case LORAMAC_STATUS_BUSY:
|
||||
return "Busy";
|
||||
case LORAMAC_STATUS_SERVICE_UNKNOWN:
|
||||
return "Service unknown";
|
||||
case LORAMAC_STATUS_PARAMETER_INVALID:
|
||||
return "Parameter invalid";
|
||||
case LORAMAC_STATUS_FREQUENCY_INVALID:
|
||||
return "Frequency invalid";
|
||||
case LORAMAC_STATUS_DATARATE_INVALID:
|
||||
return "Datarate invalid";
|
||||
case LORAMAC_STATUS_FREQ_AND_DR_INVALID:
|
||||
return "Frequency or datarate invalid";
|
||||
case LORAMAC_STATUS_NO_NETWORK_JOINED:
|
||||
return "No network joined";
|
||||
case LORAMAC_STATUS_LENGTH_ERROR:
|
||||
return "Length error";
|
||||
case LORAMAC_STATUS_REGION_NOT_SUPPORTED:
|
||||
return "Region not supported";
|
||||
case LORAMAC_STATUS_SKIPPED_APP_DATA:
|
||||
return "Skipped APP data";
|
||||
case LORAMAC_STATUS_DUTYCYCLE_RESTRICTED:
|
||||
return "Duty-cycle restricted";
|
||||
case LORAMAC_STATUS_NO_CHANNEL_FOUND:
|
||||
return "No channel found";
|
||||
case LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND:
|
||||
return "No free channel found";
|
||||
case LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME:
|
||||
return "Busy beacon reserved time";
|
||||
case LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME:
|
||||
return "Busy ping-slot window time";
|
||||
case LORAMAC_STATUS_BUSY_UPLINK_COLLISION:
|
||||
return "Busy uplink collision";
|
||||
case LORAMAC_STATUS_CRYPTO_ERROR:
|
||||
return "Crypto error";
|
||||
case LORAMAC_STATUS_FCNT_HANDLER_ERROR:
|
||||
return "FCnt handler error";
|
||||
case LORAMAC_STATUS_MAC_COMMAD_ERROR:
|
||||
return "MAC command error";
|
||||
case LORAMAC_STATUS_CLASS_B_ERROR:
|
||||
return "ClassB error";
|
||||
case LORAMAC_STATUS_CONFIRM_QUEUE_ERROR:
|
||||
return "Confirm queue error";
|
||||
case LORAMAC_STATUS_MC_GROUP_UNDEFINED:
|
||||
return "Multicast group undefined";
|
||||
case LORAMAC_STATUS_ERROR:
|
||||
return "Unknown error";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *eventinfo2str(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case LORAMAC_EVENT_INFO_STATUS_OK:
|
||||
return "OK";
|
||||
case LORAMAC_EVENT_INFO_STATUS_ERROR:
|
||||
return "Error";
|
||||
case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT:
|
||||
return "Tx timeout";
|
||||
case LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT:
|
||||
return "Rx 1 timeout";
|
||||
case LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT:
|
||||
return "Rx 2 timeout";
|
||||
case LORAMAC_EVENT_INFO_STATUS_RX1_ERROR:
|
||||
return "Rx1 error";
|
||||
case LORAMAC_EVENT_INFO_STATUS_RX2_ERROR:
|
||||
return "Rx2 error";
|
||||
case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL:
|
||||
return "Join failed";
|
||||
case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED:
|
||||
return "Downlink repeated";
|
||||
case LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR:
|
||||
return "Tx DR payload size error";
|
||||
case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS:
|
||||
return "Downlink too many frames loss";
|
||||
case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL:
|
||||
return "Address fail";
|
||||
case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL:
|
||||
return "MIC fail";
|
||||
case LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL:
|
||||
return "Multicast fail";
|
||||
case LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED:
|
||||
return "Beacon locked";
|
||||
case LORAMAC_EVENT_INFO_STATUS_BEACON_LOST:
|
||||
return "Beacon lost";
|
||||
case LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND:
|
||||
return "Beacon not found";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static LoRaMacPrimitives_t macPrimitives;
|
||||
static LoRaMacCallback_t macCallbacks;
|
||||
|
||||
void OnMacProcessNotify(void)
|
||||
{
|
||||
LoRaMacProcess();
|
||||
}
|
||||
|
||||
static void McpsConfirm(McpsConfirm_t *mcpsConfirm)
|
||||
{
|
||||
if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
|
||||
LOG_ERR("McpsRequest failed : %s",
|
||||
log_strdup(eventinfo2str(mcpsConfirm->Status)));
|
||||
} else {
|
||||
LOG_DBG("McpsRequest success!");
|
||||
send_status = true;
|
||||
}
|
||||
|
||||
k_sem_give(&lorawan_tx_sem);
|
||||
}
|
||||
|
||||
static void McpsIndication(McpsIndication_t *mcpsIndication)
|
||||
{
|
||||
if (mcpsIndication->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
|
||||
LOG_ERR("McpsIndication failed : %s",
|
||||
log_strdup(eventinfo2str(mcpsIndication->Status)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Check MCPS Indication type */
|
||||
if (mcpsIndication->RxData == true) {
|
||||
if (mcpsIndication->BufferSize != 0) {
|
||||
LOG_DBG("Rx Data: %s",
|
||||
log_strdup(mcpsIndication->Buffer));
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Compliance test based on FPort value*/
|
||||
}
|
||||
|
||||
static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm)
|
||||
{
|
||||
MibRequestConfirm_t mibGet;
|
||||
|
||||
if (mlmeConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) {
|
||||
LOG_ERR("MlmeConfirm failed : %s",
|
||||
log_strdup(eventinfo2str(mlmeConfirm->Status)));
|
||||
goto out_sem;
|
||||
}
|
||||
|
||||
switch (mlmeConfirm->MlmeRequest) {
|
||||
case MLME_JOIN:
|
||||
mibGet.Type = MIB_DEV_ADDR;
|
||||
LoRaMacMibGetRequestConfirm(&mibGet);
|
||||
LOG_INF("Joined network! DevAddr: %08x", mibGet.Param.DevAddr);
|
||||
join_status = true;
|
||||
break;
|
||||
case MLME_LINK_CHECK:
|
||||
/* Not implemented */
|
||||
LOG_INF("Link check not implemented yet!");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out_sem:
|
||||
k_sem_give(&lorawan_config_sem);
|
||||
}
|
||||
|
||||
static void MlmeIndication(MlmeIndication_t *mlmeIndication)
|
||||
{
|
||||
LOG_DBG("%s", __func__);
|
||||
}
|
||||
|
||||
static LoRaMacStatus_t lorawan_join_otaa(
|
||||
const struct lorawan_join_config *join_cfg)
|
||||
{
|
||||
MlmeReq_t mlme_req;
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
mlme_req.Type = MLME_JOIN;
|
||||
mlme_req.Req.Join.Datarate = lorawan_datarate;
|
||||
|
||||
mib_req.Type = MIB_DEV_EUI;
|
||||
mib_req.Param.DevEui = join_cfg->dev_eui;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_JOIN_EUI;
|
||||
mib_req.Param.JoinEui = join_cfg->otaa.join_eui;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_NWK_KEY;
|
||||
mib_req.Param.NwkKey = join_cfg->otaa.nwk_key;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_APP_KEY;
|
||||
mib_req.Param.JoinEui = join_cfg->otaa.app_key;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
return LoRaMacMlmeRequest(&mlme_req);
|
||||
}
|
||||
|
||||
static LoRaMacStatus_t lorawan_join_abp(
|
||||
const struct lorawan_join_config *join_cfg)
|
||||
{
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
mib_req.Type = MIB_ABP_LORAWAN_VERSION;
|
||||
mib_req.Param.AbpLrWanVersion.Value = LORAWAN_ABP_VERSION;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_NET_ID;
|
||||
mib_req.Param.NetID = 0;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_DEV_ADDR;
|
||||
mib_req.Param.DevAddr = join_cfg->abp.dev_addr;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_F_NWK_S_INT_KEY;
|
||||
mib_req.Param.FNwkSIntKey = join_cfg->abp.nwk_skey;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_S_NWK_S_INT_KEY;
|
||||
mib_req.Param.SNwkSIntKey = join_cfg->abp.nwk_skey;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_NWK_S_ENC_KEY;
|
||||
mib_req.Param.NwkSEncKey = join_cfg->abp.nwk_skey;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_APP_S_KEY;
|
||||
mib_req.Param.AppSKey = join_cfg->abp.app_skey;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
mib_req.Type = MIB_NETWORK_ACTIVATION;
|
||||
mib_req.Param.NetworkActivation = ACTIVATION_TYPE_ABP;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
return LORAMAC_STATUS_OK;
|
||||
}
|
||||
|
||||
int lorawan_join(const struct lorawan_join_config *join_cfg)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
int ret;
|
||||
|
||||
k_mutex_lock(&lorawan_join_mutex, K_FOREVER);
|
||||
|
||||
if (join_cfg->mode == LORAWAN_ACT_OTAA) {
|
||||
join_status = false;
|
||||
status = lorawan_join_otaa(join_cfg);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("OTAA join failed: %s",
|
||||
log_strdup(status2str(status)));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
LOG_DBG("Network join request sent!");
|
||||
|
||||
/*
|
||||
* We can be sure that the semaphore will be released for
|
||||
* both success and failure cases after a specific time period.
|
||||
* So we can use K_FOREVER and no need to check the return val.
|
||||
*/
|
||||
k_sem_take(&lorawan_config_sem, K_FOREVER);
|
||||
} else if (join_cfg->mode == LORAWAN_ACT_ABP) {
|
||||
status = lorawan_join_abp(join_cfg);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("ABP join failed: %s",
|
||||
log_strdup(status2str(status)));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (join_status) {
|
||||
ret = 0;
|
||||
} else {
|
||||
/* TODO: Return the exact error code */
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
k_mutex_unlock(&lorawan_join_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lorawan_set_class(enum lorawan_class dev_class)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
mib_req.Type = MIB_DEVICE_CLASS;
|
||||
|
||||
switch (dev_class) {
|
||||
case LORAWAN_CLASS_A:
|
||||
mib_req.Param.Class = CLASS_A;
|
||||
break;
|
||||
case LORAWAN_CLASS_B:
|
||||
case LORAWAN_CLASS_C:
|
||||
LOG_ERR("Device class not supported yet!");
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
|
||||
status = LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Failed to set device class: %s",
|
||||
status2str(status));
|
||||
return mac_status_to_errno[status];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lorawan_set_datarate(enum lorawan_datarate dr)
|
||||
{
|
||||
/* Bail out if using ADR */
|
||||
if (lorawan_adr_enable) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lorawan_datarate = dr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lorawan_enable_adr(bool enable)
|
||||
{
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
if (enable != lorawan_adr_enable) {
|
||||
lorawan_adr_enable = enable;
|
||||
|
||||
mib_req.Type = MIB_ADR;
|
||||
mib_req.Param.AdrEnable = lorawan_adr_enable;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
}
|
||||
}
|
||||
|
||||
int lorawan_set_conf_msg_tries(uint8_t tries)
|
||||
{
|
||||
lorawan_conf_msg_tries = tries;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lorawan_send(uint8_t port, uint8_t *data, uint8_t len, uint8_t flags)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
McpsReq_t mcpsReq;
|
||||
LoRaMacTxInfo_t txInfo;
|
||||
int ret = 0;
|
||||
bool empty_frame = false;
|
||||
|
||||
if (data == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
k_mutex_lock(&lorawan_send_mutex, K_FOREVER);
|
||||
|
||||
status = LoRaMacQueryTxPossible(len, &txInfo);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
/*
|
||||
* If status indicates an error, then most likely the payload
|
||||
* has exceeded the maximum possible length for the current
|
||||
* region and datarate. We can't do much other than sending
|
||||
* empty frame in order to flush MAC commands in stack and
|
||||
* hoping the application to lower the payload size for
|
||||
* next try.
|
||||
*/
|
||||
LOG_ERR("LoRaWAN Query Tx Possible Failed: %s",
|
||||
log_strdup(status2str(status)));
|
||||
empty_frame = true;
|
||||
mcpsReq.Type = MCPS_UNCONFIRMED;
|
||||
mcpsReq.Req.Unconfirmed.fBuffer = NULL;
|
||||
mcpsReq.Req.Unconfirmed.fBufferSize = 0;
|
||||
mcpsReq.Req.Unconfirmed.Datarate = DR_0;
|
||||
} else {
|
||||
if (flags & LORAWAN_MSG_CONFIRMED) {
|
||||
mcpsReq.Type = MCPS_CONFIRMED;
|
||||
mcpsReq.Req.Confirmed.fPort = port;
|
||||
mcpsReq.Req.Confirmed.fBuffer = data;
|
||||
mcpsReq.Req.Confirmed.fBufferSize = len;
|
||||
mcpsReq.Req.Confirmed.NbTrials = lorawan_conf_msg_tries;
|
||||
mcpsReq.Req.Confirmed.Datarate = lorawan_datarate;
|
||||
send_status = false;
|
||||
} else {
|
||||
/* default message type */
|
||||
mcpsReq.Type = MCPS_UNCONFIRMED;
|
||||
mcpsReq.Req.Unconfirmed.fPort = port;
|
||||
mcpsReq.Req.Unconfirmed.fBuffer = data;
|
||||
mcpsReq.Req.Unconfirmed.fBufferSize = len;
|
||||
mcpsReq.Req.Unconfirmed.Datarate = lorawan_datarate;
|
||||
}
|
||||
}
|
||||
|
||||
status = LoRaMacMcpsRequest(&mcpsReq);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("LoRaWAN Send failed: %s",
|
||||
log_strdup(status2str(status)));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate to the application that the current packet is not sent and
|
||||
* it has to resend the packet.
|
||||
*/
|
||||
if (empty_frame) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Wait for send confirmation */
|
||||
if (flags & LORAWAN_MSG_CONFIRMED) {
|
||||
/*
|
||||
* We can be sure that the semaphore will be released for
|
||||
* both success and failure cases after a specific time period.
|
||||
* So we can use K_FOREVER and no need to check the return val.
|
||||
*/
|
||||
k_sem_take(&lorawan_tx_sem, K_FOREVER);
|
||||
|
||||
if (send_status) {
|
||||
ret = 0;
|
||||
} else {
|
||||
/* TODO: Return the exact error code */
|
||||
ret = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
k_mutex_unlock(&lorawan_send_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lorawan_start(void)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
MibRequestConfirm_t mib_req;
|
||||
|
||||
status = LoRaMacStart();
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("Failed to start the LoRaMAC stack: %s",
|
||||
status2str(status));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TODO: Move these to a proper location */
|
||||
mib_req.Type = MIB_PUBLIC_NETWORK;
|
||||
mib_req.Param.EnablePublicNetwork = true;
|
||||
LoRaMacMibSetRequestConfirm(&mib_req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lorawan_init(const struct device *dev)
|
||||
{
|
||||
LoRaMacStatus_t status;
|
||||
|
||||
macPrimitives.MacMcpsConfirm = McpsConfirm;
|
||||
macPrimitives.MacMcpsIndication = McpsIndication;
|
||||
macPrimitives.MacMlmeConfirm = MlmeConfirm;
|
||||
macPrimitives.MacMlmeIndication = MlmeIndication;
|
||||
macCallbacks.GetBatteryLevel = NULL;
|
||||
macCallbacks.GetTemperatureLevel = NULL;
|
||||
macCallbacks.NvmContextChange = NULL;
|
||||
macCallbacks.MacProcessNotify = OnMacProcessNotify;
|
||||
|
||||
status = LoRaMacInitialization(&macPrimitives, &macCallbacks,
|
||||
LORAWAN_REGION);
|
||||
if (status != LORAMAC_STATUS_OK) {
|
||||
LOG_ERR("LoRaMacInitialization failed: %s",
|
||||
log_strdup(status2str(status)));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_DBG("LoRaMAC Initialized");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
Loading…
Reference in a new issue