diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c index 2faab85..bbdeb24 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c +++ b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.c @@ -23,24 +23,10 @@ static lr11xx_dio1_callback_t dio1_user_cb = NULL; static void *dio1_user_data = NULL; static struct lr11xx_hal_context *current_ctx = NULL; -/* Work queue for deferred DIO1 processing - * SPI operations CANNOT be done from ISR context on nRF52! - * The GPIO interrupt triggers this work item which runs in thread context. - */ -static struct k_work dio1_work; - /* BUSY pin interrupt — wakes wait_on_busy() via semaphore instead of polling */ static struct gpio_callback busy_gpio_cb; static K_SEM_DEFINE(busy_sem, 0, 1); -static void dio1_work_handler(struct k_work *work) -{ - ARG_UNUSED(work); - if (dio1_user_cb) { - dio1_user_cb(dio1_user_data); - } -} - static void busy_isr_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { @@ -116,8 +102,9 @@ static lr11xx_hal_status_t check_device_ready(struct lr11xx_hal_context *ctx) /** * @brief DIO1 GPIO interrupt callback (ISR context) * - * CRITICAL: This runs in ISR context! Cannot do SPI operations here. - * Instead, we submit work to the system work queue which runs in thread context. + * Calls the user callback directly from ISR. The caller + * (lr11xx_lora.c) submits to its own dedicated work queue, + * which is ISR-safe via k_work_submit_to_queue(). */ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) @@ -126,8 +113,9 @@ static void dio1_isr_callback(const struct device *dev, struct gpio_callback *cb ARG_UNUSED(cb); ARG_UNUSED(pins); - /* Defer to work queue - SPI ops not allowed in ISR */ - k_work_submit(&dio1_work); + if (dio1_user_cb) { + dio1_user_cb(dio1_user_data); + } } /* Public HAL API - called by Semtech driver */ @@ -179,9 +167,6 @@ int lr11xx_hal_init(struct lr11xx_hal_context *ctx) return ret; } - /* Initialize DIO1 work queue handler */ - k_work_init(&dio1_work, dio1_work_handler); - /* Set up DIO1 interrupt callback */ gpio_init_callback(&dio1_gpio_cb, dio1_isr_callback, BIT(ctx->dio1.pin)); ret = gpio_add_callback(ctx->dio1.port, &dio1_gpio_cb); diff --git a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h index e7f4760..66ea98f 100644 --- a/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h +++ b/zephcore/adapters/radio/lr11xx/lr11xx_hal_zephyr.h @@ -29,7 +29,7 @@ extern "C" { * CRITICAL: All SPI operations are protected by spi_mutex. The LR1110 radio is * accessed from two threads: * 1. Main thread: mesh event loop (noise floor calibration, TX, reconfigure) - * 2. System work queue: DIO1 interrupt handler (RX packet processing) + * 2. Dedicated DIO1 work queue: interrupt handler (RX packet processing) * Without the mutex, concurrent SPI access corrupts the LR1110 command/response * protocol, causing the BUSY pin to get stuck HIGH permanently. */ @@ -76,7 +76,8 @@ typedef void (*lr11xx_dio1_callback_t)(void *user_data); * @brief Set DIO1 interrupt callback * * @param ctx HAL context - * @param cb Callback function (called from ISR context) + * @param cb Callback function (called directly from GPIO ISR context — + * must be ISR-safe, e.g. k_work_submit_to_queue()) * @param user_data User data passed to callback */ void lr11xx_hal_set_dio1_callback(struct lr11xx_hal_context *ctx, diff --git a/zephcore/app/CompanionMesh.cpp b/zephcore/app/CompanionMesh.cpp index 149979f..2c7f1d9 100644 --- a/zephcore/app/CompanionMesh.cpp +++ b/zephcore/app/CompanionMesh.cpp @@ -1115,13 +1115,13 @@ void CompanionMesh::onRawDataRecv(mesh::Packet *packet) uint32_t CompanionMesh::getRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * 0.5f); - return getRNG()->nextInt(0, 5 * t + 1); + return getRNG()->nextInt(0, 7 * t + 1); } uint32_t CompanionMesh::getDirectRetransmitDelay(const mesh::Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * 0.2f); - return getRNG()->nextInt(0, 5 * t + 1); + return getRNG()->nextInt(0, 7 * t + 1); } uint8_t CompanionMesh::getDutyCyclePercent() const diff --git a/zephcore/app/RepeaterMesh.cpp b/zephcore/app/RepeaterMesh.cpp index 5a6dd7c..661a779 100644 --- a/zephcore/app/RepeaterMesh.cpp +++ b/zephcore/app/RepeaterMesh.cpp @@ -477,12 +477,12 @@ int RepeaterMesh::calcRxDelay(float score, uint32_t air_time) const { uint32_t RepeaterMesh::getRetransmitDelay(const mesh::Packet* packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.tx_delay_factor); - return getRNG()->nextInt(0, 3 * t + 1); + return getRNG()->nextInt(0, 7 * t + 1); } uint32_t RepeaterMesh::getDirectRetransmitDelay(const mesh::Packet* packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getPathByteLen() + packet->payload_len + 2) * _prefs.direct_tx_delay_factor); - return getRNG()->nextInt(0, 3 * t + 1); + return getRNG()->nextInt(0, 7 * t + 1); } bool RepeaterMesh::filterRecvFloodPacket(mesh::Packet* pkt) { diff --git a/zephcore/src/Mesh.cpp b/zephcore/src/Mesh.cpp index 12fd029..fc494e7 100644 --- a/zephcore/src/Mesh.cpp +++ b/zephcore/src/Mesh.cpp @@ -36,7 +36,7 @@ bool Mesh::allowPacketForward(const Packet *packet) uint32_t Mesh::getRetransmitDelay(const Packet *packet) { uint32_t t = (_radio->getEstAirtimeFor(packet->getRawLength()) * 52 / 50) / 2; - return _rng->nextInt(0, 3) * t; + return _rng->nextInt(0, 7) * t; } uint32_t Mesh::getCADFailRetryDelay() const