diff --git a/CODEOWNERS b/CODEOWNERS index 7b4cdf65485e6b..92e3313387a1a5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -344,6 +344,7 @@ /include/logging/ @nordic-krch /include/net/ @jukkar @tbursztyka @pfalcon /include/net/buf.h @jukkar @jhedberg @tbursztyka @pfalcon +/include/net/lorawan.h @Mani-Sadhasivam /include/posix/ @pfalcon /include/power/power.h @wentongwu @nashif /include/ptp_clock.h @jukkar @@ -379,6 +380,7 @@ /samples/drivers/ht16k33/ @henrikbrixandersen /samples/drivers/lora/ @Mani-Sadhasivam /samples/drivers/counter/maxim_ds3231/ @pabigot +/samples/lorawan/ @Mani-Sadhasivam /samples/net/ @jukkar @tbursztyka @pfalcon /samples/net/dns_resolve/ @jukkar @tbursztyka @pfalcon /samples/net/lwm2m_client/ @rlubos @@ -442,6 +444,7 @@ /subsys/fs/nvs/ @Laczen /subsys/logging/ @nordic-krch /subsys/logging/log_backend_net.c @nordic-krch @jukkar +/subsys/lorawan/ @Mani-Sadhasivam /subsys/mgmt/ @carlescufi @nvlsianpu /subsys/mgmt/smp_udp.c @aunsbjerg /subsys/net/buf.c @jukkar @jhedberg @tbursztyka @pfalcon diff --git a/boards/arm/adafruit_feather_m0_basic_proto/board.cmake b/boards/arm/adafruit_feather_m0_basic_proto/board.cmake index cb8bfd7c096599..05695a86d92a9a 100644 --- a/boards/arm/adafruit_feather_m0_basic_proto/board.cmake +++ b/boards/arm/adafruit_feather_m0_basic_proto/board.cmake @@ -2,6 +2,4 @@ # # SPDX-License-Identifier: Apache-2.0 -board_runner_args(bossac "--offset=0x2000") - include(${ZEPHYR_BASE}/boards/common/bossac.board.cmake) diff --git a/drivers/lora/CMakeLists.txt b/drivers/lora/CMakeLists.txt index 1a359072b4f3db..8adf4405869f3f 100644 --- a/drivers/lora/CMakeLists.txt +++ b/drivers/lora/CMakeLists.txt @@ -1,6 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 -zephyr_library_named(loramac-node) +# LoRa drivers depend 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_library_sources_ifdef(CONFIG_LORA_SHELL shell.c) zephyr_library_sources_ifdef(CONFIG_LORA_SX1276 sx1276.c) diff --git a/drivers/lora/sx1276.c b/drivers/lora/sx1276.c index 134549564d3db9..05e5306659dcc3 100644 --- a/drivers/lora/sx1276.c +++ b/drivers/lora/sx1276.c @@ -27,6 +27,8 @@ LOG_MODULE_REGISTER(sx1276); #define SX1276_REG_PA_DAC 0x4d #define SX1276_REG_VERSION 0x42 +#define BOARD_TCXO_WAKEUP_TIME 5 + static u32_t saved_time; extern DioIrqHandler *DioIrq[]; @@ -60,13 +62,15 @@ struct sx1276_data { struct k_sem data_sem; struct k_timer timer; RadioEvents_t sx1276_event; + /* TODO: Use Non-volatile memory for backup */ + volatile u32_t backup_reg[2]; u8_t *rx_buf; u8_t rx_len; s8_t snr; s16_t rssi; } dev_data; -bool SX1276CheckRfFrequency(uint32_t frequency) +bool SX1276CheckRfFrequency(u32_t frequency) { /* TODO */ return true; @@ -82,6 +86,11 @@ void SX1276SetBoardTcxo(u8_t state) /* TODO */ } +u32_t SX1276GetBoardTcxoWakeupTime(void) +{ + return BOARD_TCXO_WAKEUP_TIME; +} + void SX1276SetAntSw(u8_t opMode) { /* TODO */ @@ -99,12 +108,12 @@ void SX1276Reset(void) k_sleep(K_MSEC(6)); } -void BoardCriticalSectionBegin(uint32_t *mask) +void BoardCriticalSectionBegin(u32_t *mask) { *mask = irq_lock(); } -void BoardCriticalSectionEnd(uint32_t *mask) +void BoardCriticalSectionEnd(u32_t *mask) { irq_unlock(*mask); } @@ -176,6 +185,28 @@ static void sx1276_dio_work_handle(struct k_work *work) (*DioIrq[dio])(NULL); } +u32_t RtcGetCalendarTime(uint16_t *milliseconds) +{ + u32_t now = k_uptime_get_32(); + + *milliseconds = now; + + /* Return in seconds */ + return now / MSEC_PER_SEC; +} + +void RtcBkupWrite(u32_t data0, uint32_t data1) +{ + dev_data.backup_reg[0] = data0; + dev_data.backup_reg[1] = data1; +} + +void RtcBkupRead(u32_t *data0, uint32_t *data1) +{ + *data0 = dev_data.backup_reg[0]; + *data1 = dev_data.backup_reg[1]; +} + static void sx1276_irq_callback(struct device *dev, struct gpio_callback *cb, u32_t pins) { @@ -463,6 +494,8 @@ const struct Radio_s Radio = { .Random = SX1276Random, .SetRxConfig = SX1276SetRxConfig, .SetTxConfig = SX1276SetTxConfig, + .CheckRfFrequency = SX1276CheckRfFrequency, + .TimeOnAir = SX1276GetTimeOnAir, .Send = SX1276Send, .Sleep = SX1276SetSleep, .Standby = SX1276SetStby, @@ -472,6 +505,8 @@ const struct Radio_s Radio = { .WriteBuffer = SX1276WriteBuffer, .ReadBuffer = SX1276ReadBuffer, .SetMaxPayloadLength = SX1276SetMaxPayloadLength, + .SetPublicNetwork = SX1276SetPublicNetwork, + .GetWakeupTime = SX1276GetWakeupTime, .IrqProcess = NULL, .RxBoosted = NULL, .SetRxDutyCycle = NULL, diff --git a/include/net/lorawan.h b/include/net/lorawan.h new file mode 100644 index 00000000000000..bce2e81b073064 --- /dev/null +++ b/include/net/lorawan.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_NET_LORAWAN_H_ +#define ZEPHYR_INCLUDE_NET_LORAWAN_H_ + +/** + * @file + * @brief Public LoRaWAN APIs + */ + +#include +#include + +/** + * @brief LoRaWAN class types. + */ +enum lorawan_class { + LORAWAN_CLASS_A = 0x00, + LORAWAN_CLASS_B = 0x01, + LORAWAN_CLASS_C = 0x02, +}; + +/** + * @brief LoRaWAN activation types. + */ +enum lorawan_act_type { + LORAWAN_ACT_OTAA = 0, + LORAWAN_ACT_ABP, +}; + +/** + * @brief LoRaWAN datarate types. + */ +enum lorawan_datarate { + LORAWAN_DR_0 = 0, + LORAWAN_DR_1, + LORAWAN_DR_2, + LORAWAN_DR_3, + LORAWAN_DR_4, + LORAWAN_DR_5, + LORAWAN_DR_6, + LORAWAN_DR_7, + LORAWAN_DR_8, + LORAWAN_DR_9, + LORAWAN_DR_10, + LORAWAN_DR_11, + LORAWAN_DR_12, + LORAWAN_DR_13, + LORAWAN_DR_14, + LORAWAN_DR_15, +}; + +struct lorawan_mib_config { + enum lorawan_class lw_class; + u8_t *dev_eui; + u8_t *join_eui; + u8_t *app_eui; + u8_t *app_key; + u8_t *nwk_key; + u32_t system_max_rs_error; + bool pub_nw; + bool adr_enable; +}; + +/** + * @brief Configure the LoRaWAN stack + * + * Configure the LoRaWAN stack using MIB (Mac Information Base) parameters. + * + * @param mib_config MIB configuration + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_config(struct lorawan_mib_config *mib_config); + +/** + * @brief Join the LoRaWAN network + * + * Join the LoRaWAN network using either OTAA or AWB. + * + * @param datarate Datarate to be used for network join + * @param mode Activation mode + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode); + +/** + * @brief Send data to the LoRaWAN network + * + * Send data to the connected LoRaWAN network. + * + * @param port Port to be used for sending data. Must be set if the + * payload is not empty. + * @param datarate Datarate to be used for sending data + * @param data Data buffer to be sent + * @param len Length of the buffer to be sent. Maximum length of this + * buffer is 255 bytes but the actual payload size varies with + * region and datarate. + * @param confirm Use confirmed messages + * @param tries Number of tries needed for sending the data in case the Ack + * from server is not received + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_send(u8_t port, enum lorawan_datarate datarate, + u8_t *data, u8_t len, bool confirm, u8_t tries); +/** + * @brief Check if there is uncollected received data from the LoRaWAN network + * + * Check if there is uncolllected received data from the LoRaWAN network + * + * @return 0 or positive number indicating the number of received messages + * waiting to be read from the buffer, or negative errno code if failure + */ +int lorawan_receive_available(); + +/** + * @brief Check if any uncollected received incoming data was discarded + * + * Check if any uncollected received incoming data was discarded due to + * insufficient free space in the RX buffer. + * + * @return 0 or positive number indicating the number of discarded messages + */ +int lorawan_receive_discarded(); + +/** + * @brief Read uncollected received data from the LoRaWAN network + * + * Read uncollected received data from the LoRaWAN network. This function will + * return each received message only once - each read opration removes the + * oldest downlink measage from the buffer of received unread messages. + * + * @param port Port at which the message arrived + * @param data Data buffer where received data shall be placed + * @param len Size of the \p data buffer available. Messages with + * payloads larger than the buffer will be truncated + * + * @return 0 if successful, negative errno code if failure + */ +int lorawan_receive_read(u8_t *port, u8_t *data, u8_t len); + +#endif /* ZEPHYR_INCLUDE_NET_LORAWAN_H_ */ diff --git a/modules/Kconfig.loramac-node b/modules/Kconfig.loramac-node index fc775d30b25ec7..0b004df7959193 100644 --- a/modules/Kconfig.loramac-node +++ b/modules/Kconfig.loramac-node @@ -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 diff --git a/samples/lorawan/class_a/CMakeLists.txt b/samples/lorawan/class_a/CMakeLists.txt new file mode 100644 index 00000000000000..7938d79d8c8cca --- /dev/null +++ b/samples/lorawan/class_a/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(lorawan) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/lorawan/class_a/boards/96b_wistrio.conf b/samples/lorawan/class_a/boards/96b_wistrio.conf new file mode 100644 index 00000000000000..be022a25e699d3 --- /dev/null +++ b/samples/lorawan/class_a/boards/96b_wistrio.conf @@ -0,0 +1 @@ +CONFIG_SPI_1=y diff --git a/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay new file mode 100644 index 00000000000000..490b65735b52c6 --- /dev/null +++ b/samples/lorawan/class_a/boards/adafruit_feather_m0_basic_proto.overlay @@ -0,0 +1,34 @@ +/* + * This overlay suits Adafrut Feather M0 LoRa edition. + * + * Copyritght (c) 2020: R3-IoT Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&sercom4 { + status = "okay"; + compatible = "atmel,sam0-spi"; + + cs-gpios = <&porta 6 0>; + + dipo = <0>; + dopo = <1>; + + lora: sx1276@0 { + compatible = "semtech,sx1276"; + reg = <0>; + label = "sx1276"; + reset-gpios = <&portb 13 0>; + dio-gpios = <&porta 9 0>; + + spi-max-frequency = <1000000>; + }; +}; + +/ { + + aliases { + lora0 = &lora; + }; +}; diff --git a/samples/lorawan/class_a/prj.conf b/samples/lorawan/class_a/prj.conf new file mode 100644 index 00000000000000..06c22c37928507 --- /dev/null +++ b/samples/lorawan/class_a/prj.conf @@ -0,0 +1,11 @@ +CONFIG_LOG=y +CONFIG_SPI=y +CONFIG_GPIO=y +CONFIG_NEWLIB_LIBC=y +CONFIG_LORA=y +CONFIG_LORA_SX1276=y +CONFIG_LORAWAN=y +CONFIG_LORAWAN_USE_RX_RING_BUFFER=n + +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_LORAWAN_USE_RX_RING_BUFFER=y diff --git a/samples/lorawan/class_a/sample.yaml b/samples/lorawan/class_a/sample.yaml new file mode 100644 index 00000000000000..768c9d1d27dfee --- /dev/null +++ b/samples/lorawan/class_a/sample.yaml @@ -0,0 +1,8 @@ +common: + tags: lorawan +sample: + description: Demonstration of Class-A LoRaWAN functionality + name: LoRaWAN Class-A +tests: + samples.lorawan.class_a: + platform_whitelist: 96b_wistrio diff --git a/samples/lorawan/class_a/src/main.c b/samples/lorawan/class_a/src/main.c new file mode 100644 index 00000000000000..68f540b5d5b70f --- /dev/null +++ b/samples/lorawan/class_a/src/main.c @@ -0,0 +1,127 @@ +/* + * Class A LoRaWAN sample application + * + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#define DEFAULT_RADIO_NODE DT_ALIAS(lora0) +BUILD_ASSERT(DT_HAS_NODE_STATUS_OKAY(DEFAULT_RADIO_NODE), + "No default LoRa radio specified in DT"); +#define DEFAULT_RADIO DT_LABEL(DEFAULT_RADIO_NODE) + +/* Customize based on network configuration */ +#define LORAWAN_DEV_EUI { 0xDD, 0xEE, 0xAA, 0xDD, 0xBB, 0xEE,\ + 0xEE, 0xFF } +#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00 } +#define LORAWAN_APP_KEY { 0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE,\ + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88,\ + 0x09, 0xCF, 0x4F, 0x3C } +#define LORAWAN_DEFAULT_DATARATE LORAWAN_DR_0 + +#define DELAY K_MSEC(5000) +#define DL_MSG_LEN 256 + +#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL +#include +LOG_MODULE_REGISTER(lorawan_class_a); + +char data[] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'}; + +void main(void) +{ + struct device *lora_dev; + struct lorawan_mib_config mib_config; + int ret; + int dl_msgs_no; + int i; + uint8_t port; + uint8_t dl_msg[DL_MSG_LEN] = {'\0'}; + u8_t dev_eui[] = LORAWAN_DEV_EUI; + u8_t join_eui[] = LORAWAN_JOIN_EUI; + u8_t app_eui[] = LORAWAN_APP_EUI; + u8_t app_key[] = LORAWAN_APP_KEY; + + lora_dev = device_get_binding(DEFAULT_RADIO); + if (!lora_dev) { + LOG_ERR("%s Device not found", DEFAULT_RADIO); + return; + } + + mib_config.lw_class = LORAWAN_CLASS_A; + mib_config.dev_eui = dev_eui; + mib_config.join_eui = join_eui; + mib_config.app_eui = app_eui; + mib_config.app_key = app_key; + mib_config.nwk_key = app_key; + mib_config.pub_nw = true; /* Connecting to a public network */ + mib_config.adr_enable = true; + mib_config.system_max_rs_error = 20; + + LOG_INF("Configuring LoRaWAN stack"); + ret = lorawan_config(&mib_config); + if (ret < 0) { + LOG_ERR("lorawan_config failed: %d", ret); + return; + } + + ret = -1; + + while (ret < 0) { + LOG_INF("Joining network over OTAA"); + ret = lorawan_join_network(LORAWAN_DEFAULT_DATARATE, LORAWAN_ACT_OTAA); + if (ret < 0) { + LOG_ERR("lorawan_join_network failed: %d", ret); + k_sleep(K_MSEC(3000)); + } else { + break; + } + } + + while (1) { + k_sleep(DELAY); + LOG_INF("Sending data..."); + ret = lorawan_send(2, LORAWAN_DEFAULT_DATARATE, data, + sizeof(data), true, 1); + /* + * Note: The stack may return -EAGAIN if the provided data + * length exceeds the maximum possible one for the region and + * datarate. But since we are just sending the same data here, + * we'll just continue. + */ + if (ret == -EAGAIN) { + LOG_ERR("lorawan_send failed: %d. Retrying...", ret); + continue; + } + + if (ret < 0) { + LOG_ERR("lorawan_send failed: %d. Retrying...", ret); + continue; + } + + LOG_INF("Data sent!"); + k_sleep(K_MSEC(1000)); + + dl_msgs_no = lorawan_receive_available(); + if (dl_msgs_no >= 0) { + LOG_INF("%d downlink msgs available", dl_msgs_no); + for (i=0; i= 0 ) { + LOG_INF("Msg at port %d of length %d: %s", + port, + ret, + log_strdup(dl_msg)); + } + } + } + } +} \ No newline at end of file diff --git a/subsys/CMakeLists.txt b/subsys/CMakeLists.txt index a203f5bd34db58..a92b3b1fbd54d3 100644 --- a/subsys/CMakeLists.txt +++ b/subsys/CMakeLists.txt @@ -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) diff --git a/subsys/Kconfig b/subsys/Kconfig index 615ae9762b6fec..6531ff9f307fe2 100644 --- a/subsys/Kconfig +++ b/subsys/Kconfig @@ -17,6 +17,8 @@ source "subsys/fs/Kconfig" source "subsys/logging/Kconfig" +source "subsys/lorawan/Kconfig" + source "subsys/mgmt/Kconfig" source "subsys/net/Kconfig" diff --git a/subsys/lorawan/CMakeLists.txt b/subsys/lorawan/CMakeLists.txt new file mode 100644 index 00000000000000..141eb972b73106 --- /dev/null +++ b/subsys/lorawan/CMakeLists.txt @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 + +# lorawan.c depend 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() + +if(CONFIG_LORAMAC_REGION_AS923) + zephyr_compile_definitions(-DREGION_AS923) +elseif(CONFIG_LORAMAC_REGION_AU915) + zephyr_compile_definitions(-DREGION_AU915) +elseif(CONFIG_LORAMAC_REGION_CN470) + zephyr_compile_definitions(-DREGION_CN470) +elseif(CONFIG_LORAMAC_REGION_CN779) + zephyr_compile_definitions(-DREGION_CN779) +elseif(CONFIG_LORAMAC_REGION_EU433) + zephyr_compile_definitions(-DREGION_EU433) +elseif(CONFIG_LORAMAC_REGION_EU868) + zephyr_compile_definitions(-DREGION_EU868) +elseif(CONFIG_LORAMAC_REGION_KR920) + zephyr_compile_definitions(-DREGION_KR920) +elseif(CONFIG_LORAMAC_REGION_IN865) + zephyr_compile_definitions(-DREGION_IN865) +elseif(CONFIG_LORAMAC_REGION_US915) + zephyr_compile_definitions(-DREGION_US915) +elseif(CONFIG_LORAMAC_REGION_RU864) + zephyr_compile_definitions(-DREGION_RU864) +endif() + +zephyr_library_sources_ifdef(CONFIG_LORAWAN lorawan.c) diff --git a/subsys/lorawan/Kconfig b/subsys/lorawan/Kconfig new file mode 100644 index 00000000000000..cc2924bfec2295 --- /dev/null +++ b/subsys/lorawan/Kconfig @@ -0,0 +1,80 @@ +# LoRaWAN configuration options + +# Copyright (c) 2019 Manivannan Sadhasivam +# 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_IN865 + help + Select the LoRaWAN 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 + +config LORAWAN_USE_RX_RING_BUFFER + bool "Use Ring Buffer for Downlink" + default False + depends on RING_BUFFER + help + This value enables a ring buffer to be used for storing incoming + downlink messages instead of only the latest message being available. + Oldest messages are discarded in case of incoming payload not having + enough space in the buffer. + +if LORAWAN_USE_RX_RING_BUFFER + +config LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS + int "Downlink Ring Buffer Size in 32-bit words" + default 8 + help + This value defines the maximum number 32-bit words used in a buffer + for downlink messages. Must be at least N+2 where N is the largest + expected downlink payload in 32-bit chunks. + +endif # LORAWAN_USE_RX_RING_BUFFER + +endif # LORAWAN diff --git a/subsys/lorawan/lorawan.c b/subsys/lorawan/lorawan.c new file mode 100644 index 00000000000000..dcd6de9773de07 --- /dev/null +++ b/subsys/lorawan/lorawan.c @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2019 Manivannan Sadhasivam + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER +#include +#endif //def CONFIG_LORAWAN_USE_RX_RING_BUFFER + +#include + +#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 +#elif + #error "Atleast one LoRaWAN region should be selected" +#endif + +#define CEIL_32(x) ((unsigned int)(((((x))+31U)/32U)*32U)) +#define SIZE_BYTES_TO_WORDS(x) (CEIL_32(x*8)/32) + + +#define LORAWAN_PLD_MAX_SIZE_BYTES 242U +#define LORAWAN_PLD_MAX_SIZE_WORDS SIZE_BYTES_TO_WORDS(LORAWAN_PLD_MAX_SIZE_BYTES) + +#define LOG_LEVEL CONFIG_LORAWAN_LOG_LEVEL +#include +LOG_MODULE_REGISTER(lorawan); + +K_SEM_DEFINE(mlme_confirm_sem, 0, 1); +K_SEM_DEFINE(mcps_confirm_sem, 0, 1); + +K_MUTEX_DEFINE(lorawan_join_mutex); +K_MUTEX_DEFINE(lorawan_send_mutex); + +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER + +#define RX_RING_BUF_SIZE_WORDS (CONFIG_LORAWAN_RX_RING_BUFFER_MAX_SIZE_WORDS) +struct rx_ring_buf { + struct ring_buf rb; + u32_t buffer[RX_RING_BUF_SIZE_WORDS]; +}; + +struct rx_ring_buf rx_buf; + +#else + +uint8_t rx_buffer[LORAWAN_PLD_MAX_SIZE_BYTES]; +uint8_t rx_buffer_stored_len; +uint8_t rx_buffer_port; + +#endif //def CONFIG_LORAWAN_USE_RX_RING_BUFFER + +static uint16_t rx_buf_avail_elem; +static uint16_t rx_buf_discarded_elem; +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER +static uint32_t payload_tmp[LORAWAN_PLD_MAX_SIZE_WORDS]; +static uint8_t payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; +#endif +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; + } +} + +/* + * MAC status and Event status to Zephyr error code conversion. + * Direct mapping is not possible as statuses often indicate the domain from + * which the error originated rather than its cause or meaning. -EINVAL has been + * used as a general error code because those usually result from incorrect + * configuration. + */ +const int mac_status_to_errno[] = { + 0, /* LORAMAC_STATUS_OK */ + -EBUSY, /* LORAMAC_STATUS_BUSY */ + -ENOPROTOOPT, /* LORAMAC_STATUS_SERVICE_UNKNOWN */ + -EINVAL, /* LORAMAC_STATUS_PARAMETER_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQUENCY_INVALID */ + -EINVAL, /* LORAMAC_STATUS_DATARATE_INVALID */ + -EINVAL, /* LORAMAC_STATUS_FREQ_AND_DR_INVALID */ + -ENOTCONN, /* LORAMAC_STATUS_NO_NETWORK_JOINED */ + -EMSGSIZE, /* LORAMAC_STATUS_LENGTH_ERROR */ + -EPFNOSUPPORT, /* LORAMAC_STATUS_REGION_NOT_SUPPORTED */ + -EMSGSIZE, /* LORAMAC_STATUS_SKIPPED_APP_DATA */ + -ECONNREFUSED, /* LORAMAC_STATUS_DUTYCYCLE_RESTRICTED */ + -ENOTCONN, /* LORAMAC_STATUS_NO_CHANNEL_FOUND */ + -ENOTCONN, /* LORAMAC_STATUS_NO_FREE_CHANNEL_FOUND */ + -EBUSY, /* LORAMAC_STATUS_BUSY_BEACON_RESERVED_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_PING_SLOT_WINDOW_TIME */ + -EBUSY, /* LORAMAC_STATUS_BUSY_UPLINK_COLLISION */ + -EINVAL, /* LORAMAC_STATUS_CRYPTO_ERROR */ + -EINVAL, /* LORAMAC_STATUS_FCNT_HANDLER_ERROR */ + -EINVAL, /* LORAMAC_STATUS_MAC_COMMAD_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CLASS_B_ERROR */ + -EINVAL, /* LORAMAC_STATUS_CONFIRM_QUEUE_ERROR */ + -EINVAL /* LORAMAC_STATUS_MC_GROUP_UNDEFINED */ +}; + +const int mac_event_info_to_errno[] = { + 0, /* LORAMAC_EVENT_INFO_STATUS_OK */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_ERROR */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX1_TIMEOUT */ + -ETIMEDOUT, /* LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX1_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_RX2_ERROR */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED */ + -EMSGSIZE, /* LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR */ + -ECONNRESET, /* LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL */ + -EACCES, /* LORAMAC_EVENT_INFO_STATUS_MIC_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED */ + -EINVAL, /* LORAMAC_EVENT_INFO_STATUS_BEACON_LOST */ + -EINVAL /* LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND */ +}; + +static LoRaMacPrimitives_t macPrimitives; +static LoRaMacCallback_t macCallbacks; + +static LoRaMacEventInfoStatus_t last_mcps_confirm_status; +static LoRaMacEventInfoStatus_t last_mlme_confirm_status; +static LoRaMacEventInfoStatus_t last_mcps_indication_status; +static LoRaMacEventInfoStatus_t last_mlme_indication_status; + +/** + * @brief Read a downlink message from the ring buffer. + * + * Read a downlink message from the ring buffer and place its payload in the + * provided container. The message is consumed in the process. If the container + * is shorter than the available payload any excess data will be discarded and + * only as much of the payload as possible will be output. + * + * @param port Container for the message's port + * @param payload Container for the message's payload + * @param len Lenght in bytes of the \p payload container. + * + * @retval non-negative integer indicating the number of read payload bytes + * successfully copied to the provided container + * @retval -EAGAIN No messages in the buffer. + */ +static int rx_buf_get(u8_t *port, u8_t *payload, u16_t len) +{ + int len_to_copy; + +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER + int ret; + u16_t rx_buffer_stored_len; + + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; + ret = ring_buf_item_get(&rx_buf.rb, &rx_buffer_stored_len, + port, payload_tmp, &payload_tmp_size); + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; + + if ( ret < 0 ) { + return ret; + } + + rx_buf_avail_elem--; + len_to_copy = rx_buffer_stored_len < len ? rx_buffer_stored_len : len; + memcpy(payload, payload_tmp, len_to_copy); +#else + len_to_copy = rx_buffer_stored_len < len ? rx_buffer_stored_len : len; + rx_buf_avail_elem = 0; + *port = rx_buffer_port; + rx_buffer_port = 0; + memcpy(payload, rx_buffer, len_to_copy); +#endif + return len_to_copy; +} + +/** + * @brief Put a downlink message into the ring buffer. + * + * Put a downlink message intothe ring buffer. If the buffer doesn not have + * sufficient free space oldest messages will be discarded for as long as + * possible until sufficient space is available. + * + * @param port Message's port number + * @param payload Container for the message's payload + * @param len \p payload size in bytes. + * + * @retval 0 On success + * @retval -EMSGSIZE Message too large for the buffer +*/ +static int rx_buf_put(u8_t port, u8_t *payload, u16_t len) +{ +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER + int ret; + u16_t type_tmp; + u8_t val_tmp; + memcpy(payload_tmp, payload, len); + + ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, + SIZE_BYTES_TO_WORDS(len)); + + while (ret == -EMSGSIZE) { + /* not enough room for the data item -> remove the oldes one */ + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; + ret = ring_buf_item_get(&rx_buf.rb, &type_tmp, &val_tmp, payload_tmp, + &payload_tmp_size); + if (ret == -EAGAIN) { + /* Buffer doesn't have any items -> the one we are + * trying to add won't fit any way. */ + return -EMSGSIZE; + } + LOG_DBG("Discarded ring buff elemem size %d", payload_tmp_size); + rx_buf_avail_elem--; + rx_buf_discarded_elem++; + payload_tmp_size = LORAWAN_PLD_MAX_SIZE_WORDS; + + memcpy(payload_tmp, payload, len); + + ret = ring_buf_item_put(&rx_buf.rb, len, port, payload_tmp, + SIZE_BYTES_TO_WORDS(len)); + } + + + rx_buf_avail_elem++; +#else + memcpy(rx_buffer, payload, len); + + rx_buf_avail_elem = 1; + rx_buffer_port = port; + rx_buffer_stored_len = len; +#endif + + return 0; +} + +/** + * @brief Return the number of downlink messages available. + * + * Return the number of downlink messages available in the buffer + * + * @retval number of downlink messages available in the buffer + */ +static int rx_buf_avail() +{ + return rx_buf_avail_elem; +} + +/** + * @brief Return the number of discarded downlink messages since last call. + * + * Returns the number of discarded downlink messages since this funcion has been + * called last. + * + * @retval number of discarded characters + */ +static int rx_buf_discarded() +{ + int retval = rx_buf_discarded_elem; + rx_buf_discarded_elem = 0; + return retval; +} + +static void OnMacProcessNotify(void) +{ + LoRaMacProcess(); +} + +static void McpsConfirm(McpsConfirm_t *mcpsConfirm) +{ + LOG_DBG("Received McpsConfirm (for McpsRequest %d)", + mcpsConfirm->McpsRequest); + + if (mcpsConfirm->Status != LORAMAC_EVENT_INFO_STATUS_OK) { + LOG_ERR("McpsRequest failed : %s", + log_strdup(eventinfo2str(mcpsConfirm->Status))); + } else { + LOG_DBG("McpsRequest success!"); + } + + last_mcps_confirm_status = mcpsConfirm->Status; + k_sem_give(&mcps_confirm_sem); +} + +static void McpsIndication(McpsIndication_t *mcpsIndication) +{ + int ret; + LOG_DBG("Received McpsIndication %d", mcpsIndication->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) { + LOG_DBG("RxData set"); + if (mcpsIndication->BufferSize != 0) { + LOG_DBG("Rx %dB Data: %s", + mcpsIndication->BufferSize, + log_strdup(mcpsIndication->Buffer)); + + ret = rx_buf_put(mcpsIndication->Port, + mcpsIndication->Buffer, + mcpsIndication->BufferSize); + if (ret == -EMSGSIZE) { + LOG_WRN("Rx buff too small for DL size %d", + mcpsIndication->BufferSize); + } + } + } + + last_mcps_indication_status = mcpsIndication->Status; + + /* TODO: Compliance test based on FPort value*/ +} + +static void MlmeConfirm(MlmeConfirm_t *mlmeConfirm) +{ + MibRequestConfirm_t mibGet; + + LOG_DBG("Received MlmeConfirm (for MlmeRequest %d)", + mlmeConfirm->MlmeRequest); + + 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); + break; + case MLME_LINK_CHECK: + /* Not implemented */ + break; + default: + break; + } + +out_sem: + last_mlme_confirm_status = mlmeConfirm->Status; + k_sem_give(&mlme_confirm_sem); +} + +static void MlmeIndication(MlmeIndication_t *mlmeIndication) +{ + LOG_DBG("Received MlmeIndication %d", mlmeIndication->MlmeIndication); + last_mlme_indication_status = mlmeIndication->Status; +} + +int lorawan_config(struct lorawan_mib_config *mib_config) +{ + MibRequestConfirm_t mibReq; + + mibReq.Type = MIB_NWK_KEY; + mibReq.Param.NwkKey = mib_config->nwk_key; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEV_EUI; + mibReq.Param.DevEui = mib_config->dev_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_JOIN_EUI; + mibReq.Param.JoinEui = mib_config->join_eui; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_DEVICE_CLASS; + mibReq.Param.Class = mib_config->lw_class; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_ADR; + mibReq.Param.AdrEnable = mib_config->adr_enable; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_PUBLIC_NETWORK; + mibReq.Param.EnablePublicNetwork = mib_config->pub_nw; + LoRaMacMibSetRequestConfirm(&mibReq); + + mibReq.Type = MIB_SYSTEM_MAX_RX_ERROR; + mibReq.Param.SystemMaxRxError = mib_config->system_max_rs_error; + LoRaMacMibSetRequestConfirm(&mibReq); + + return 0; +} + +static LoRaMacStatus_t lorawan_join_otaa(enum lorawan_datarate datarate) +{ + MlmeReq_t mlmeReq; + + mlmeReq.Type = MLME_JOIN; + mlmeReq.Req.Join.Datarate = datarate; + + return LoRaMacMlmeRequest(&mlmeReq); +} + +int lorawan_join_network(enum lorawan_datarate datarate, + enum lorawan_act_type mode) +{ + LoRaMacStatus_t status; + int ret = 0; + + k_mutex_lock(&lorawan_join_mutex, K_FOREVER); + + if (mode == LORAWAN_ACT_OTAA) { + status = lorawan_join_otaa(datarate); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("OTAA join failed: %s", + log_strdup(status2str(status))); + ret = mac_status_to_errno[status]; + 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(&mlme_confirm_sem, K_FOREVER); + if (last_mlme_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mlme_confirm_status]; + goto out; + } + } else { + ret = -EINVAL; + } + +out: + k_mutex_unlock(&lorawan_join_mutex); + return ret; +} + +int lorawan_send(u8_t port, enum lorawan_datarate datarate, u8_t *data, + u8_t len, bool confirm, u8_t tries) +{ + 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 this returns false, 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 (confirm == false) { + mcpsReq.Type = MCPS_UNCONFIRMED; + mcpsReq.Req.Unconfirmed.fPort = port; + mcpsReq.Req.Unconfirmed.fBuffer = data; + mcpsReq.Req.Unconfirmed.fBufferSize = len; + mcpsReq.Req.Unconfirmed.Datarate = datarate; + } else { + mcpsReq.Type = MCPS_CONFIRMED; + mcpsReq.Req.Confirmed.fPort = port; + mcpsReq.Req.Confirmed.fBuffer = data; + mcpsReq.Req.Confirmed.fBufferSize = len; + mcpsReq.Req.Confirmed.NbTrials = tries; + mcpsReq.Req.Confirmed.Datarate = datarate; + } + } + + status = LoRaMacMcpsRequest(&mcpsReq); + if (status != LORAMAC_STATUS_OK) { + LOG_ERR("LoRaWAN Send failed: %s", + log_strdup(status2str(status))); + ret = mac_status_to_errno[status]; + goto out; + } + + /* + * Indicate 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 (confirm) { + /* + * 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(&mcps_confirm_sem, K_FOREVER); + + if (last_mcps_confirm_status != LORAMAC_EVENT_INFO_STATUS_OK) { + ret = mac_event_info_to_errno[last_mcps_confirm_status]; + } + } + +out: + k_mutex_unlock(&lorawan_send_mutex); + return ret; +} + +int lorawan_receive_available() +{ + return rx_buf_avail(); +} + +int lorawan_receive_read(u8_t *port, u8_t *data, u8_t len) +{ + return rx_buf_get(port, data, len); +} + +int lorawan_receive_discarded() +{ + return rx_buf_discarded(); +} + +static int lorawan_init(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; + } + + +#if CONFIG_LORAWAN_USE_RX_RING_BUFFER + ring_buf_init(&rx_buf.rb, RX_RING_BUF_SIZE_WORDS, rx_buf.buffer); +#endif + + rx_buf_avail_elem = 0; + rx_buf_discarded_elem = 0; + + LoRaMacStart(); + + LOG_DBG("LoRaMAC Initialized"); + + return 0; +} + +SYS_INIT(lorawan_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); diff --git a/west.yml b/west.yml index fb15152c283a52..0edef9e485b427 100644 --- a/west.yml +++ b/west.yml @@ -98,7 +98,7 @@ manifest: revision: 724f7e2a4519d7e1d40ef330042682dea950c991 path: modules/lib/open-amp - name: loramac-node - revision: 29e516ec585b1a909af2b5f1c60d83e7d4d563e3 + revision: 85f21c03ed765ce36ac917978a2eff5fae1dbccf path: modules/lib/loramac-node - name: openthread revision: a83d18cf18bc8cd431ad6704ad0bf0e6d08c99d7