0002-drivers-lora-native-sx126x-implement-Channel-Activit.patch 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. From 4aa014031d7bacdc08e2b5d5e1cfadbf0c5a964d Mon Sep 17 00:00:00 2001
  2. From: ZiB <zib@zi3.xyz>
  3. Date: Sat, 7 Nov 2026 03:35:03 -0800
  4. Subject: [PATCH 2/3] drivers: lora: native: sx126x: implement Channel Activity
  5. Detection
  6. Wires the sx126x native driver into the lora.h CAD API that was
  7. already fully specified there (lora_cad()/lora_cad_async(),
  8. lora_cad_mode, and the cad sub-struct in lora_modem_config) but never
  9. had a backend: .cad/.cad_async were both unset in the driver's
  10. DEVICE_API struct.
  11. Adds sx126x_set_cad_params()/sx126x_set_cad(), a new SX126X_STATE_CAD
  12. state following the existing CAS-based busy protocol, and a
  13. CadDone/CadActivityDetected IRQ path alongside the existing TX/RX/
  14. timeout ones - folded into SX126X_IRQ_MASK_REQUIRED since those bits
  15. only ever fire after a SetCAD.
  16. Signed-off-by: ZiB <zib@zi3.xyz>
  17. ---
  18. drivers/lora/native/sx126x/sx126x.c | 284 ++++++++++++++++++++++-
  19. drivers/lora/native/sx126x/sx126x.h | 13 ++
  20. drivers/lora/native/sx126x/sx126x_regs.h | 11 +
  21. include/zephyr/drivers/lora/sx126x.h | 21 +-
  22. 4 files changed, 317 insertions(+), 12 deletions(-)
  23. diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c
  24. index c3f9d63f3f5..8ba8416ed0c 100644
  25. --- a/drivers/lora/native/sx126x/sx126x.c
  26. +++ b/drivers/lora/native/sx126x/sx126x.c
  27. @@ -22,6 +22,11 @@ LOG_MODULE_REGISTER(sx126x, CONFIG_LORA_LOG_LEVEL);
  28. #define SX126X_SF56_MIN_PREAMBLE_LEN 12
  29. +/* IRQ sources the driver's own TX/RX/CAD/timeout handling needs on DIO1. */
  30. +#define SX126X_IRQ_MASK_REQUIRED \
  31. + (SX126X_IRQ_TX_DONE | SX126X_IRQ_RX_DONE | SX126X_IRQ_RX_TX_TIMEOUT | SX126X_IRQ_CRC_ERR | \
  32. + SX126X_IRQ_CAD_DONE | SX126X_IRQ_CAD_ACTIVITY_DETECTED)
  33. +
  34. #ifdef CONFIG_LORA_GFSK
  35. #define SX126X_IS_GFSK(data) ((data)->gfsk)
  36. #else
  37. @@ -406,6 +411,102 @@ static int sx126x_set_rx(const struct device *dev, uint32_t timeout_ms)
  38. return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX, buf, 3);
  39. }
  40. +static int sx126x_cad_symbol_num_to_reg(enum lora_cad_symbol_num symbol_num, uint8_t *reg)
  41. +{
  42. + switch (symbol_num) {
  43. + case LORA_CAD_SYMB_1:
  44. + *reg = SX126X_CAD_ON_1_SYMB;
  45. + return 0;
  46. + case LORA_CAD_SYMB_2:
  47. + *reg = SX126X_CAD_ON_2_SYMB;
  48. + return 0;
  49. + case LORA_CAD_SYMB_4:
  50. + *reg = SX126X_CAD_ON_4_SYMB;
  51. + return 0;
  52. + case LORA_CAD_SYMB_8:
  53. + *reg = SX126X_CAD_ON_8_SYMB;
  54. + return 0;
  55. + case LORA_CAD_SYMB_16:
  56. + *reg = SX126X_CAD_ON_16_SYMB;
  57. + return 0;
  58. + default:
  59. + return -EINVAL;
  60. + }
  61. +}
  62. +
  63. +/*
  64. + * Per-SF detDetMin/cadDetPeak/cadSymbolNum defaults, from Semtech AN1200.48
  65. + * ("SX126x CAD Performance Evaluation") Tables 1 and 43 - the "best
  66. + * settings" rows, which pick the symbol count each SF needs for reliable
  67. + * detection without false positives, rather than the lower-power 2-symbol
  68. + * alternative also published there. AN1200.48 only characterizes 125kHz
  69. + * and 500kHz; a config using any other bandwidth must supply explicit
  70. + * cad.detection_peak/detection_minimum (and usually symbol_num) of its own.
  71. + */
  72. +struct sx126x_cad_defaults {
  73. + uint8_t det_min;
  74. + uint8_t det_peak;
  75. + enum lora_cad_symbol_num symbol_num;
  76. +};
  77. +
  78. +static int sx126x_cad_lookup_defaults(enum lora_signal_bandwidth bandwidth,
  79. + enum lora_datarate datarate,
  80. + struct sx126x_cad_defaults *out)
  81. +{
  82. + static const struct sx126x_cad_defaults bw_125khz[] = {
  83. + [SF_7] = { 10, 22, LORA_CAD_SYMB_2 },
  84. + [SF_8] = { 10, 22, LORA_CAD_SYMB_2 },
  85. + [SF_9] = { 10, 23, LORA_CAD_SYMB_4 },
  86. + [SF_10] = { 10, 24, LORA_CAD_SYMB_4 },
  87. + [SF_11] = { 10, 25, LORA_CAD_SYMB_4 },
  88. + [SF_12] = { 10, 28, LORA_CAD_SYMB_4 },
  89. + };
  90. + static const struct sx126x_cad_defaults bw_500khz[] = {
  91. + [SF_7] = { 10, 21, LORA_CAD_SYMB_4 },
  92. + [SF_8] = { 10, 22, LORA_CAD_SYMB_4 },
  93. + [SF_9] = { 10, 22, LORA_CAD_SYMB_4 },
  94. + [SF_10] = { 10, 23, LORA_CAD_SYMB_4 },
  95. + [SF_11] = { 10, 25, LORA_CAD_SYMB_4 },
  96. + [SF_12] = { 10, 29, LORA_CAD_SYMB_8 },
  97. + };
  98. + const struct sx126x_cad_defaults *table;
  99. +
  100. + if (bandwidth == BW_125_KHZ) {
  101. + table = bw_125khz;
  102. + } else if (bandwidth == BW_500_KHZ) {
  103. + table = bw_500khz;
  104. + } else {
  105. + return -ENOTSUP;
  106. + }
  107. +
  108. + if (datarate < SF_7 || datarate > SF_12) {
  109. + return -EINVAL;
  110. + }
  111. +
  112. + *out = table[datarate];
  113. + return 0;
  114. +}
  115. +
  116. +static int sx126x_set_cad_params(const struct device *dev, uint8_t symbol_num_reg,
  117. + uint8_t det_peak, uint8_t det_min, uint8_t exit_mode,
  118. + uint32_t timeout)
  119. +{
  120. + uint8_t buf[7];
  121. +
  122. + buf[0] = symbol_num_reg;
  123. + buf[1] = det_peak;
  124. + buf[2] = det_min;
  125. + buf[3] = exit_mode;
  126. + sys_put_be24(timeout, &buf[4]);
  127. +
  128. + return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_CAD_PARAMS, buf, 7);
  129. +}
  130. +
  131. +static int sx126x_set_cad(const struct device *dev)
  132. +{
  133. + return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_CAD, NULL, 0);
  134. +}
  135. +
  136. static int sx126x_get_rx_buffer_status(const struct device *dev,
  137. uint8_t *payload_len, uint8_t *offset)
  138. {
  139. @@ -558,9 +659,9 @@ static int sx126x_chip_init(const struct device *dev)
  140. return ret;
  141. }
  142. - /* Configure IRQs on DIO1: TX done, RX done, timeout */
  143. - uint16_t irq_mask = SX126X_IRQ_TX_DONE | SX126X_IRQ_RX_DONE |
  144. - SX126X_IRQ_RX_TX_TIMEOUT | SX126X_IRQ_CRC_ERR;
  145. + /* Configure IRQs on DIO1: TX done, RX done, CAD done, timeout */
  146. + uint16_t irq_mask = SX126X_IRQ_MASK_REQUIRED;
  147. +
  148. ret = sx126x_set_dio_irq_params(dev, irq_mask, irq_mask, 0, 0);
  149. if (ret < 0) {
  150. LOG_ERR("Set IRQ params failed: %d", ret);
  151. @@ -804,6 +905,33 @@ out:
  152. }
  153. }
  154. +static void sx126x_handle_irq_cad_done(const struct device *dev, uint16_t irq_status)
  155. +{
  156. + struct sx126x_data *data = dev->data;
  157. + struct sx126x_cad_result result = {
  158. + .activity_detected = (irq_status & SX126X_IRQ_CAD_ACTIVITY_DETECTED) != 0,
  159. + };
  160. +
  161. + LOG_DBG("CAD done: activity=%d", result.activity_detected);
  162. +
  163. + /* CAD_ONLY already left the chip in STDBY_RC; this just folds it
  164. + * back into the driver's own rest state.
  165. + */
  166. + sx126x_set_sleep(dev);
  167. +
  168. + if (data->cad_cb != NULL) {
  169. + lora_cad_cb cb = data->cad_cb;
  170. + void *user_data = data->cad_cb_user_data;
  171. +
  172. + data->cad_cb = NULL;
  173. + data->cad_cb_user_data = NULL;
  174. + cb(dev, result.activity_detected, user_data);
  175. + return;
  176. + }
  177. +
  178. + k_msgq_put(&data->cad_msgq, &result, K_NO_WAIT);
  179. +}
  180. +
  181. static void sx126x_handle_irq_timeout(const struct device *dev)
  182. {
  183. struct sx126x_data *data = dev->data;
  184. @@ -874,6 +1002,10 @@ static void sx126x_irq_work_handler(struct k_work *work)
  185. sx126x_handle_irq_timeout(dev);
  186. }
  187. + if (irq_status & SX126X_IRQ_CAD_DONE) {
  188. + sx126x_handle_irq_cad_done(dev, irq_status);
  189. + }
  190. +
  191. /* Re-enable the DIO1 interrupt for the next event (unless sleeping) */
  192. if (atomic_get(&data->state) != SX126X_REST_STATE) {
  193. sx126x_hal_dio1_irq_enable(dev);
  194. @@ -1573,6 +1705,148 @@ static int sx126x_lora_rssi(const struct device *dev, int16_t *rssi)
  195. return ret;
  196. }
  197. +/*
  198. + * Shared setup for lora_cad()/lora_cad_async(): resolve CAD parameters,
  199. + * claim the radio and issue SetCadParams + SetCAD. On return 0, the SPI
  200. + * transaction is complete and the chip is already searching; the caller's
  201. + * msgq/cb delivers the CadDone result. On error, state and mutex are
  202. + * already unwound.
  203. + */
  204. +static int sx126x_cad_start(const struct device *dev)
  205. +{
  206. + struct sx126x_data *data = dev->data;
  207. + struct sx126x_cad_defaults defaults;
  208. + enum lora_cad_symbol_num symbol_num;
  209. + uint8_t det_peak, det_min, symbol_num_reg;
  210. + int ret;
  211. +
  212. + if (!data->config_valid) {
  213. + LOG_ERR("Not configured");
  214. + return -EINVAL;
  215. + }
  216. +
  217. + if (SX126X_IS_GFSK(data)) {
  218. + LOG_ERR("CAD is a LoRa-only operation");
  219. + return -ENOTSUP;
  220. + }
  221. +
  222. + symbol_num = data->config.cad.symbol_num;
  223. + det_peak = data->config.cad.detection_peak;
  224. + det_min = data->config.cad.detection_minimum;
  225. +
  226. + if (symbol_num == 0 || det_peak == 0 || det_min == 0) {
  227. + ret = sx126x_cad_lookup_defaults(data->config.bandwidth,
  228. + data->config.datarate, &defaults);
  229. + if (ret < 0) {
  230. + LOG_ERR("No CAD defaults for this bandwidth/SF; "
  231. + "set cad.symbol_num/detection_peak/detection_minimum explicitly");
  232. + return ret;
  233. + }
  234. + if (symbol_num == 0) {
  235. + symbol_num = defaults.symbol_num;
  236. + }
  237. + if (det_peak == 0) {
  238. + det_peak = defaults.det_peak;
  239. + }
  240. + if (det_min == 0) {
  241. + det_min = defaults.det_min;
  242. + }
  243. + }
  244. +
  245. + ret = sx126x_cad_symbol_num_to_reg(symbol_num, &symbol_num_reg);
  246. + if (ret < 0) {
  247. + LOG_ERR("Invalid CAD symbol count: %d", symbol_num);
  248. + return ret;
  249. + }
  250. +
  251. + if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_CAD)) {
  252. + LOG_ERR("Busy");
  253. + return -EBUSY;
  254. + }
  255. +
  256. + k_mutex_lock(&data->lock, K_FOREVER);
  257. +
  258. + ret = sx126x_ensure_ready(dev);
  259. + if (ret < 0) {
  260. + goto out_error;
  261. + }
  262. +
  263. + ret = sx126x_set_cad_params(dev, symbol_num_reg, det_peak, det_min,
  264. + SX126X_CAD_EXIT_MODE_CAD_ONLY, 0);
  265. + if (ret < 0) {
  266. + goto out_error;
  267. + }
  268. +
  269. + ret = sx126x_set_cad(dev);
  270. + if (ret < 0) {
  271. + goto out_error;
  272. + }
  273. +
  274. + k_mutex_unlock(&data->lock);
  275. + return 0;
  276. +
  277. +out_error:
  278. + sx126x_set_sleep(dev);
  279. + k_mutex_unlock(&data->lock);
  280. + return ret;
  281. +}
  282. +
  283. +static int sx126x_lora_cad(const struct device *dev, k_timeout_t timeout)
  284. +{
  285. + struct sx126x_data *data = dev->data;
  286. + struct sx126x_cad_result result;
  287. + int ret;
  288. +
  289. + k_msgq_purge(&data->cad_msgq);
  290. +
  291. + ret = sx126x_cad_start(dev);
  292. + if (ret < 0) {
  293. + return ret;
  294. + }
  295. +
  296. + ret = k_msgq_get(&data->cad_msgq, &result, timeout);
  297. + if (ret < 0) {
  298. + LOG_DBG("CAD timeout");
  299. + k_mutex_lock(&data->lock, K_FOREVER);
  300. + sx126x_set_standby(dev, SX126X_STANDBY_RC);
  301. + sx126x_set_sleep(dev);
  302. + k_mutex_unlock(&data->lock);
  303. + return -ETIMEDOUT;
  304. + }
  305. +
  306. + return result.activity_detected ? 1 : 0;
  307. +}
  308. +
  309. +static int sx126x_lora_cad_async(const struct device *dev, lora_cad_cb cb, void *user_data)
  310. +{
  311. + struct sx126x_data *data = dev->data;
  312. + int ret;
  313. +
  314. + if (cb == NULL) {
  315. + k_mutex_lock(&data->lock, K_FOREVER);
  316. + data->cad_cb = NULL;
  317. + data->cad_cb_user_data = NULL;
  318. + if (atomic_cas(&data->state, SX126X_STATE_CAD, SX126X_STATE_IDLE)) {
  319. + sx126x_set_standby(dev, SX126X_STANDBY_RC);
  320. + sx126x_set_sleep(dev);
  321. + }
  322. + k_mutex_unlock(&data->lock);
  323. + return 0;
  324. + }
  325. +
  326. + data->cad_cb = cb;
  327. + data->cad_cb_user_data = user_data;
  328. +
  329. + ret = sx126x_cad_start(dev);
  330. + if (ret < 0) {
  331. + data->cad_cb = NULL;
  332. + data->cad_cb_user_data = NULL;
  333. + return ret;
  334. + }
  335. +
  336. + return 0;
  337. +}
  338. +
  339. static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency,
  340. int8_t tx_power, uint16_t duration)
  341. {
  342. @@ -1644,6 +1918,8 @@ static DEVICE_API(lora, sx126x_lora_api) = {
  343. .airtime = sx126x_lora_airtime,
  344. .test_cw = sx126x_lora_test_cw,
  345. .rssi = sx126x_lora_rssi,
  346. + .cad = sx126x_lora_cad,
  347. + .cad_async = sx126x_lora_cad_async,
  348. };
  349. #ifdef CONFIG_PM_DEVICE
  350. @@ -1673,6 +1949,8 @@ static int sx126x_init(const struct device *dev)
  351. sizeof(struct sx126x_tx_result), 1);
  352. k_msgq_init(&data->rx_msgq, (char *)&data->rx_result,
  353. sizeof(struct sx126x_rx_result), 1);
  354. + k_msgq_init(&data->cad_msgq, (char *)&data->cad_result,
  355. + sizeof(struct sx126x_cad_result), 1);
  356. k_work_init(&data->irq_work, sx126x_irq_work_handler);
  357. data->dev = dev;
  358. atomic_set(&data->state, SX126X_STATE_IDLE);
  359. diff --git a/drivers/lora/native/sx126x/sx126x.h b/drivers/lora/native/sx126x/sx126x.h
  360. index 0ea5154e901..5f22f620f35 100644
  361. --- a/drivers/lora/native/sx126x/sx126x.h
  362. +++ b/drivers/lora/native/sx126x/sx126x.h
  363. @@ -19,6 +19,7 @@ enum sx126x_state {
  364. SX126X_STATE_TX,
  365. SX126X_STATE_RX,
  366. SX126X_STATE_RX_DUTY_CYCLE,
  367. + SX126X_STATE_CAD,
  368. };
  369. struct sx126x_tx_result {
  370. @@ -32,6 +33,10 @@ struct sx126x_rx_result {
  371. int status;
  372. };
  373. +struct sx126x_cad_result {
  374. + bool activity_detected;
  375. +};
  376. +
  377. struct sx126x_data {
  378. struct sx126x_hal_data hal;
  379. @@ -63,6 +68,14 @@ struct sx126x_data {
  380. lora_recv_cb rx_cb;
  381. void *rx_cb_user_data;
  382. + /* CAD completion via message queue */
  383. + struct k_msgq cad_msgq;
  384. + struct sx126x_cad_result cad_result;
  385. +
  386. + /* Async CAD callback */
  387. + lora_cad_cb cad_cb;
  388. + void *cad_cb_user_data;
  389. +
  390. /* RX duty cycle parameters (in SX126x timeout ticks) */
  391. struct {
  392. uint32_t rx_period;
  393. diff --git a/drivers/lora/native/sx126x/sx126x_regs.h b/drivers/lora/native/sx126x/sx126x_regs.h
  394. index 9b342f473f3..819ef81b2fd 100644
  395. --- a/drivers/lora/native/sx126x/sx126x_regs.h
  396. +++ b/drivers/lora/native/sx126x/sx126x_regs.h
  397. @@ -201,6 +201,17 @@
  398. #define SX126X_LORA_IQ_STANDARD 0x00
  399. #define SX126X_LORA_IQ_INVERTED 0x01
  400. +/* CAD symbol count (for SET_CAD_PARAMS) */
  401. +#define SX126X_CAD_ON_1_SYMB 0x00
  402. +#define SX126X_CAD_ON_2_SYMB 0x01
  403. +#define SX126X_CAD_ON_4_SYMB 0x02
  404. +#define SX126X_CAD_ON_8_SYMB 0x03
  405. +#define SX126X_CAD_ON_16_SYMB 0x04
  406. +
  407. +/* CAD exit mode (for SET_CAD_PARAMS) */
  408. +#define SX126X_CAD_EXIT_MODE_CAD_ONLY 0x00
  409. +#define SX126X_CAD_EXIT_MODE_CAD_RX 0x01
  410. +
  411. /* TX Power Ramp Time (for SET_TX_PARAMS) */
  412. #define SX126X_RAMP_10_US 0x00
  413. #define SX126X_RAMP_20_US 0x01
  414. diff --git a/include/zephyr/drivers/lora/sx126x.h b/include/zephyr/drivers/lora/sx126x.h
  415. index 7e134f434af..4052f86aa41 100644
  416. --- a/include/zephyr/drivers/lora/sx126x.h
  417. +++ b/include/zephyr/drivers/lora/sx126x.h
  418. @@ -21,17 +21,20 @@ extern "C" {
  419. * @name SX126x IRQ source bits
  420. *
  421. * Mirror the chip's GetIrqStatus bit positions (DS.SX1261-2.W.APP,
  422. - * 13.3.1), for use with @ref sx126x_irq_timestamp_get.
  423. + * 13.3.1), for use with @ref sx126x_irq_timestamp_get. Limited to the
  424. + * sources always enabled on DIO1 (SX126X_IRQ_MASK_REQUIRED in the
  425. + * driver) - the finer-grained demod sub-stages (preamble detected, sync
  426. + * word valid, header valid) aren't exposed here: bench testing found no
  427. + * useful added precision over RX_DONE/TX_DONE, which is where the
  428. + * driver's own TX/RX dwell time is stable enough that what's actually
  429. + * being measured past that point is MCU/radio clock skew, not timing
  430. + * resolution.
  431. * @{
  432. */
  433. -#define SX126X_IRQ_TS_TX_DONE BIT(0)
  434. -#define SX126X_IRQ_TS_RX_DONE BIT(1)
  435. -#define SX126X_IRQ_TS_PREAMBLE_DETECTED BIT(2)
  436. -#define SX126X_IRQ_TS_SYNC_WORD_VALID BIT(3)
  437. -#define SX126X_IRQ_TS_HEADER_VALID BIT(4)
  438. -#define SX126X_IRQ_TS_HEADER_ERR BIT(5)
  439. -#define SX126X_IRQ_TS_CRC_ERR BIT(6)
  440. -#define SX126X_IRQ_TS_RX_TX_TIMEOUT BIT(9)
  441. +#define SX126X_IRQ_TS_TX_DONE BIT(0)
  442. +#define SX126X_IRQ_TS_RX_DONE BIT(1)
  443. +#define SX126X_IRQ_TS_CRC_ERR BIT(6)
  444. +#define SX126X_IRQ_TS_RX_TX_TIMEOUT BIT(9)
  445. /** @} */
  446. /**
  447. --
  448. 2.54.0 (Apple Git-157)