LoraApi callbacks refactored to events (#632)
This commit is contained in:
committed by
GitHub
parent
b7577f2328
commit
d2442bedb4
@@ -1,21 +1,326 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <tactility/drivers/lora.h>
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <tactility/device.h>
|
||||
#include <tactility/driver.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#define LORA_API(device) ((const struct LoraApi*)device_get_driver(device)->api)
|
||||
|
||||
struct LoraEventMutex {
|
||||
Mutex handle {};
|
||||
LoraEventMutex() { mutex_construct(&handle); }
|
||||
~LoraEventMutex() { mutex_destruct(&handle); }
|
||||
};
|
||||
|
||||
// Each event kind gets its own intrusive singly-linked subscription list, keyed by device, and
|
||||
// its own coarse-grained mutex. Notifying a subscriber never invokes caller code (just a struct
|
||||
// copy and a task_event_group_signal), so there is no reentrancy concern requiring a
|
||||
// snapshot-then-unlock dance.
|
||||
static LoraStateEventSubscription* lora_state_event_subscriptions = nullptr;
|
||||
static LoraEventMutex lora_state_event_subscriptions_mutex;
|
||||
|
||||
static LoraRxEventSubscription* lora_rx_event_subscriptions = nullptr;
|
||||
static LoraEventMutex lora_rx_event_subscriptions_mutex;
|
||||
|
||||
static LoraTxEventSubscription* lora_tx_event_subscriptions = nullptr;
|
||||
static LoraEventMutex lora_tx_event_subscriptions_mutex;
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct Device* lora_find_first_registered_device() {
|
||||
struct Device* found = nullptr;
|
||||
device_for_each_of_type(&LORA_TYPE, &found, [](struct Device* dev, void* ctx) -> bool {
|
||||
*static_cast<struct Device**>(ctx) = dev;
|
||||
return false;
|
||||
});
|
||||
return found;
|
||||
// region State events
|
||||
|
||||
error_t lora_state_event_subscribe(LoraStateEventSubscription* sub, TaskEventGroup* event_group, Device* device) {
|
||||
uint32_t bit;
|
||||
error_t claim_result = task_event_group_claim_bit(event_group, &bit);
|
||||
if (claim_result != ERROR_NONE) {
|
||||
return claim_result;
|
||||
}
|
||||
|
||||
mutex_lock(&lora_state_event_subscriptions_mutex.handle);
|
||||
|
||||
// Avoid cyclic subscription list that would loop forever
|
||||
for (LoraStateEventSubscription* existing = lora_state_event_subscriptions; existing != nullptr; existing = existing->internal.next) {
|
||||
if (existing == sub) {
|
||||
mutex_unlock(&lora_state_event_subscriptions_mutex.handle);
|
||||
task_event_group_release_bit(event_group, bit);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
sub->bit = bit;
|
||||
sub->internal.device = device;
|
||||
sub->internal.event_group = event_group;
|
||||
sub->internal.head = 0;
|
||||
sub->internal.count = 0;
|
||||
sub->internal.next = lora_state_event_subscriptions;
|
||||
lora_state_event_subscriptions = sub;
|
||||
mutex_unlock(&lora_state_event_subscriptions_mutex.handle);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t lora_state_event_unsubscribe(LoraStateEventSubscription* sub) {
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_state_event_subscriptions_mutex.handle);
|
||||
for (LoraStateEventSubscription** link = &lora_state_event_subscriptions; *link != nullptr; link = &(*link)->internal.next) {
|
||||
if (*link == sub) {
|
||||
*link = sub->internal.next;
|
||||
result = ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lora_state_event_subscriptions_mutex.handle);
|
||||
|
||||
if (result == ERROR_NONE) {
|
||||
task_event_group_release_bit(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_state_event_emit(Device* device, enum LoraRadioState state) {
|
||||
LoraStateEvent stamped_event { .timestamp = get_micros_since_boot(), .state = state };
|
||||
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_state_event_subscriptions_mutex.handle);
|
||||
for (LoraStateEventSubscription* sub = lora_state_event_subscriptions; sub != nullptr; sub = sub->internal.next) {
|
||||
if (sub->internal.device != device) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub->internal.count >= LORA_STATE_EVENT_QUEUE_CAPACITY) {
|
||||
result = ERROR_RESOURCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t tail = (sub->internal.head + sub->internal.count) % LORA_STATE_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.queue[tail] = stamped_event;
|
||||
sub->internal.count++;
|
||||
if (result != ERROR_RESOURCE) {
|
||||
result = ERROR_NONE;
|
||||
}
|
||||
task_event_group_signal(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
mutex_unlock(&lora_state_event_subscriptions_mutex.handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_state_event_poll(LoraStateEventSubscription* sub, LoraStateEvent* out_event) {
|
||||
mutex_lock(&lora_state_event_subscriptions_mutex.handle);
|
||||
bool has_event = sub->internal.count > 0;
|
||||
if (has_event) {
|
||||
*out_event = sub->internal.queue[sub->internal.head];
|
||||
sub->internal.head = (sub->internal.head + 1) % LORA_STATE_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.count--;
|
||||
}
|
||||
mutex_unlock(&lora_state_event_subscriptions_mutex.handle);
|
||||
return has_event ? ERROR_NONE : ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region RX events
|
||||
|
||||
error_t lora_rx_event_subscribe(LoraRxEventSubscription* sub, TaskEventGroup* event_group, Device* device) {
|
||||
uint32_t bit;
|
||||
error_t claim_result = task_event_group_claim_bit(event_group, &bit);
|
||||
if (claim_result != ERROR_NONE) {
|
||||
return claim_result;
|
||||
}
|
||||
|
||||
mutex_lock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
|
||||
// Avoid cyclic subscription list that would loop forever
|
||||
for (LoraRxEventSubscription* existing = lora_rx_event_subscriptions; existing != nullptr; existing = existing->internal.next) {
|
||||
if (existing == sub) {
|
||||
mutex_unlock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
task_event_group_release_bit(event_group, bit);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
sub->bit = bit;
|
||||
sub->internal.device = device;
|
||||
sub->internal.event_group = event_group;
|
||||
sub->internal.head = 0;
|
||||
sub->internal.count = 0;
|
||||
sub->internal.next = lora_rx_event_subscriptions;
|
||||
lora_rx_event_subscriptions = sub;
|
||||
mutex_unlock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t lora_rx_event_unsubscribe(LoraRxEventSubscription* sub) {
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
for (LoraRxEventSubscription** link = &lora_rx_event_subscriptions; *link != nullptr; link = &(*link)->internal.next) {
|
||||
if (*link == sub) {
|
||||
*link = sub->internal.next;
|
||||
result = ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
|
||||
if (result == ERROR_NONE) {
|
||||
task_event_group_release_bit(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_rx_event_emit(Device* device, const uint8_t* data, size_t length, float rssi, float snr) {
|
||||
LoraRxEvent stamped_event {};
|
||||
stamped_event.timestamp = get_micros_since_boot();
|
||||
stamped_event.length = std::min(length, sizeof(stamped_event.data));
|
||||
std::memcpy(stamped_event.data, data, stamped_event.length);
|
||||
stamped_event.rssi = rssi;
|
||||
stamped_event.snr = snr;
|
||||
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
for (LoraRxEventSubscription* sub = lora_rx_event_subscriptions; sub != nullptr; sub = sub->internal.next) {
|
||||
if (sub->internal.device != device) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub->internal.count >= LORA_RX_EVENT_QUEUE_CAPACITY) {
|
||||
result = ERROR_RESOURCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t tail = (sub->internal.head + sub->internal.count) % LORA_RX_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.queue[tail] = stamped_event;
|
||||
sub->internal.count++;
|
||||
if (result != ERROR_RESOURCE) {
|
||||
result = ERROR_NONE;
|
||||
}
|
||||
task_event_group_signal(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
mutex_unlock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_rx_event_poll(LoraRxEventSubscription* sub, LoraRxEvent* out_event) {
|
||||
mutex_lock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
bool has_event = sub->internal.count > 0;
|
||||
if (has_event) {
|
||||
*out_event = sub->internal.queue[sub->internal.head];
|
||||
sub->internal.head = (sub->internal.head + 1) % LORA_RX_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.count--;
|
||||
}
|
||||
mutex_unlock(&lora_rx_event_subscriptions_mutex.handle);
|
||||
return has_event ? ERROR_NONE : ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region TX events
|
||||
|
||||
error_t lora_tx_event_subscribe(LoraTxEventSubscription* sub, TaskEventGroup* event_group, Device* device) {
|
||||
uint32_t bit;
|
||||
error_t claim_result = task_event_group_claim_bit(event_group, &bit);
|
||||
if (claim_result != ERROR_NONE) {
|
||||
return claim_result;
|
||||
}
|
||||
|
||||
mutex_lock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
|
||||
// Avoid cyclic subscription list that would loop forever
|
||||
for (LoraTxEventSubscription* existing = lora_tx_event_subscriptions; existing != nullptr; existing = existing->internal.next) {
|
||||
if (existing == sub) {
|
||||
mutex_unlock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
task_event_group_release_bit(event_group, bit);
|
||||
return ERROR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
sub->bit = bit;
|
||||
sub->internal.device = device;
|
||||
sub->internal.event_group = event_group;
|
||||
sub->internal.head = 0;
|
||||
sub->internal.count = 0;
|
||||
sub->internal.next = lora_tx_event_subscriptions;
|
||||
lora_tx_event_subscriptions = sub;
|
||||
mutex_unlock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t lora_tx_event_unsubscribe(LoraTxEventSubscription* sub) {
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
for (LoraTxEventSubscription** link = &lora_tx_event_subscriptions; *link != nullptr; link = &(*link)->internal.next) {
|
||||
if (*link == sub) {
|
||||
*link = sub->internal.next;
|
||||
result = ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
|
||||
if (result == ERROR_NONE) {
|
||||
task_event_group_release_bit(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_tx_event_emit(Device* device, LoraTxId id, enum LoraTransmissionState state) {
|
||||
LoraTxEvent stamped_event { .timestamp = get_micros_since_boot(), .id = id, .state = state };
|
||||
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
for (LoraTxEventSubscription* sub = lora_tx_event_subscriptions; sub != nullptr; sub = sub->internal.next) {
|
||||
if (sub->internal.device != device) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub->internal.count >= LORA_TX_EVENT_QUEUE_CAPACITY) {
|
||||
result = ERROR_RESOURCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t tail = (sub->internal.head + sub->internal.count) % LORA_TX_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.queue[tail] = stamped_event;
|
||||
sub->internal.count++;
|
||||
if (result != ERROR_RESOURCE) {
|
||||
result = ERROR_NONE;
|
||||
}
|
||||
task_event_group_signal(sub->internal.event_group, sub->bit);
|
||||
}
|
||||
mutex_unlock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t lora_tx_event_poll(LoraTxEventSubscription* sub, LoraTxEvent* out_event) {
|
||||
mutex_lock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
bool has_event = sub->internal.count > 0;
|
||||
if (has_event) {
|
||||
*out_event = sub->internal.queue[sub->internal.head];
|
||||
sub->internal.head = (sub->internal.head + 1) % LORA_TX_EVENT_QUEUE_CAPACITY;
|
||||
sub->internal.count--;
|
||||
}
|
||||
mutex_unlock(&lora_tx_event_subscriptions_mutex.handle);
|
||||
return has_event ? ERROR_NONE : ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
error_t lora_get_radio_state(struct Device* device, enum LoraRadioState* state) {
|
||||
return LORA_API(device)->get_radio_state(device, state);
|
||||
}
|
||||
@@ -52,30 +357,6 @@ error_t lora_transmit(struct Device* device, const uint8_t* data, size_t length,
|
||||
return LORA_API(device)->transmit(device, data, length, id);
|
||||
}
|
||||
|
||||
error_t lora_add_rx_callback(struct Device* device, void* callback_context, LoraRxCallback callback) {
|
||||
return LORA_API(device)->add_rx_callback(device, callback_context, callback);
|
||||
}
|
||||
|
||||
error_t lora_remove_rx_callback(struct Device* device, LoraRxCallback callback) {
|
||||
return LORA_API(device)->remove_rx_callback(device, callback);
|
||||
}
|
||||
|
||||
error_t lora_add_state_callback(struct Device* device, void* callback_context, LoraStateCallback callback) {
|
||||
return LORA_API(device)->add_state_callback(device, callback_context, callback);
|
||||
}
|
||||
|
||||
error_t lora_remove_state_callback(struct Device* device, LoraStateCallback callback) {
|
||||
return LORA_API(device)->remove_state_callback(device, callback);
|
||||
}
|
||||
|
||||
error_t lora_add_tx_callback(struct Device* device, void* callback_context, LoraTxCallback callback) {
|
||||
return LORA_API(device)->add_tx_callback(device, callback_context, callback);
|
||||
}
|
||||
|
||||
error_t lora_remove_tx_callback(struct Device* device, LoraTxCallback callback) {
|
||||
return LORA_API(device)->remove_tx_callback(device, callback);
|
||||
}
|
||||
|
||||
const struct DeviceType LORA_TYPE = {
|
||||
.name = "lora"
|
||||
};
|
||||
|
||||
@@ -397,7 +397,6 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
|
||||
// wifi_auto_scan
|
||||
DEFINE_MODULE_SYMBOL(wifi_auto_scan_set_paused),
|
||||
// drivers/lora
|
||||
DEFINE_MODULE_SYMBOL(lora_find_first_registered_device),
|
||||
DEFINE_MODULE_SYMBOL(lora_get_radio_state),
|
||||
DEFINE_MODULE_SYMBOL(lora_set_enabled),
|
||||
DEFINE_MODULE_SYMBOL(lora_set_modulation),
|
||||
@@ -407,12 +406,15 @@ const struct ModuleSymbol KERNEL_SYMBOLS[] = {
|
||||
DEFINE_MODULE_SYMBOL(lora_set_parameter),
|
||||
DEFINE_MODULE_SYMBOL(lora_get_parameter),
|
||||
DEFINE_MODULE_SYMBOL(lora_transmit),
|
||||
DEFINE_MODULE_SYMBOL(lora_add_rx_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_remove_rx_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_add_state_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_remove_state_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_add_tx_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_remove_tx_callback),
|
||||
DEFINE_MODULE_SYMBOL(lora_state_event_subscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_state_event_unsubscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_state_event_poll),
|
||||
DEFINE_MODULE_SYMBOL(lora_rx_event_subscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_rx_event_unsubscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_rx_event_poll),
|
||||
DEFINE_MODULE_SYMBOL(lora_tx_event_subscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_tx_event_unsubscribe),
|
||||
DEFINE_MODULE_SYMBOL(lora_tx_event_poll),
|
||||
DEFINE_MODULE_SYMBOL(LORA_TYPE),
|
||||
// drivers/usb_host_hid
|
||||
DEFINE_MODULE_SYMBOL(usb_host_hid_is_connected),
|
||||
|
||||
Reference in New Issue
Block a user