lora: sx12xx_common: thread-safe API
Ensure that the modem is not asked to perform new operations before the previous operation completes. An atomic variable is used instead of a mutex as multiple threads need to release the lock. A semaphore isn't used as there is no indication whether `k_sem_give` gives the semaphore or not, which is required to determine if `Radio.Sleep()` should be run. `Radio.Sleep()` is only ever run by the context that successfully releases the modem usage, to guard against double calls. Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
parent
4b370fb5e3
commit
586a4bfc7d
1 changed files with 71 additions and 6 deletions
|
@ -8,6 +8,7 @@
|
|||
#include <drivers/gpio.h>
|
||||
#include <drivers/lora.h>
|
||||
#include <logging/log.h>
|
||||
#include <sys/atomic_builtin.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
/* LoRaMac-node specific includes */
|
||||
|
@ -15,11 +16,16 @@
|
|||
|
||||
#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);
|
||||
|
||||
static struct sx12xx_data {
|
||||
struct k_poll_signal *operation_done;
|
||||
RadioEvents_t events;
|
||||
atomic_t modem_usage;
|
||||
uint8_t *rx_buf;
|
||||
uint8_t rx_len;
|
||||
int8_t snr;
|
||||
|
@ -47,16 +53,54 @@ int __sx12xx_configure_pin(const struct device **dev, const char *controller,
|
|||
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)
|
||||
{
|
||||
Radio.Sleep();
|
||||
|
||||
dev_data.rx_buf = payload;
|
||||
dev_data.rx_len = size;
|
||||
dev_data.rssi = rssi;
|
||||
dev_data.snr = snr;
|
||||
|
||||
modem_release(&dev_data);
|
||||
|
||||
if (dev_data.operation_done) {
|
||||
k_poll_signal_raise(dev_data.operation_done, 0);
|
||||
}
|
||||
|
@ -64,12 +108,17 @@ static void sx12xx_ev_rx_done(uint8_t *payload, uint16_t size, int16_t rssi,
|
|||
|
||||
static void sx12xx_ev_tx_done(void)
|
||||
{
|
||||
Radio.Sleep();
|
||||
modem_release(&dev_data);
|
||||
}
|
||||
|
||||
int sx12xx_lora_send(const struct device *dev, uint8_t *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
/* Ensure available, decremented by sx12xx_ev_tx_done */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
Radio.SetMaxPayloadLength(MODEM_LORA, data_len);
|
||||
|
||||
Radio.Send(data, data_len);
|
||||
|
@ -87,6 +136,11 @@ int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
|
|||
&done);
|
||||
int ret;
|
||||
|
||||
/* Ensure available, decremented by sx12xx_ev_rx_done or on timeout */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Store operation signal */
|
||||
dev_data.operation_done = &done;
|
||||
|
||||
|
@ -96,9 +150,7 @@ int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
|
|||
ret = k_poll(&evt, 1, timeout);
|
||||
if (ret < 0) {
|
||||
LOG_INF("Receive timeout");
|
||||
/* Manually transition to sleep mode on timeout */
|
||||
dev_data.operation_done = NULL;
|
||||
Radio.Sleep();
|
||||
modem_release(&dev_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -128,6 +180,11 @@ int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
|
|||
int sx12xx_lora_config(const struct device *dev,
|
||||
struct lora_modem_config *config)
|
||||
{
|
||||
/* Ensure available, decremented after configuration */
|
||||
if (!modem_acquire(&dev_data)) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
Radio.SetChannel(config->frequency);
|
||||
|
||||
if (config->tx) {
|
||||
|
@ -143,6 +200,7 @@ int sx12xx_lora_config(const struct device *dev,
|
|||
false, 0, 0, false, true);
|
||||
}
|
||||
|
||||
modem_release(&dev_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -150,12 +208,19 @@ 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.events.TxDone = sx12xx_ev_tx_done;
|
||||
dev_data.events.RxDone = sx12xx_ev_rx_done;
|
||||
Radio.Init(&dev_data.events);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue