1 /*
2 * Copyright (c) 2019 Manivannan Sadhasivam
3 * Copyright (c) 2020 Grinn
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/lora.h>
10 #include <zephyr/logging/log.h>
11 #include <zephyr/sys/atomic.h>
12 #include <zephyr/kernel.h>
13
14 /* LoRaMac-node specific includes */
15 #include <radio.h>
16
17 #include "sx12xx_common.h"
18
19 #define STATE_FREE 0
20 #define STATE_BUSY 1
21 #define STATE_CLEANUP 2
22
23 LOG_MODULE_REGISTER(sx12xx_common, CONFIG_LORA_LOG_LEVEL);
24
25 struct sx12xx_rx_params {
26 uint8_t *buf;
27 uint8_t *size;
28 int16_t *rssi;
29 int8_t *snr;
30 };
31
32 static struct sx12xx_data {
33 const struct device *dev;
34 struct k_poll_signal *operation_done;
35 lora_recv_cb async_rx_cb;
36 RadioEvents_t events;
37 struct lora_modem_config tx_cfg;
38 atomic_t modem_usage;
39 struct sx12xx_rx_params rx_params;
40 } dev_data;
41
__sx12xx_configure_pin(const struct gpio_dt_spec * gpio,gpio_flags_t flags)42 int __sx12xx_configure_pin(const struct gpio_dt_spec *gpio, gpio_flags_t flags)
43 {
44 int err;
45
46 if (!device_is_ready(gpio->port)) {
47 LOG_ERR("GPIO device not ready %s", gpio->port->name);
48 return -ENODEV;
49 }
50
51 err = gpio_pin_configure_dt(gpio, flags);
52 if (err) {
53 LOG_ERR("Cannot configure gpio %s %d: %d", gpio->port->name,
54 gpio->pin, err);
55 return err;
56 }
57
58 return 0;
59 }
60
61 /**
62 * @brief Attempt to acquire the modem for operations
63 *
64 * @param data common sx12xx data struct
65 *
66 * @retval true if modem was acquired
67 * @retval false otherwise
68 */
modem_acquire(struct sx12xx_data * data)69 static inline bool modem_acquire(struct sx12xx_data *data)
70 {
71 return atomic_cas(&data->modem_usage, STATE_FREE, STATE_BUSY);
72 }
73
74 /**
75 * @brief Safely release the modem from any context
76 *
77 * This function can be called from any context and guarantees that the
78 * release operations will only be run once.
79 *
80 * @param data common sx12xx data struct
81 *
82 * @retval true if modem was released by this function
83 * @retval false otherwise
84 */
modem_release(struct sx12xx_data * data)85 static bool modem_release(struct sx12xx_data *data)
86 {
87 /* Increment atomic so both acquire and release will fail */
88 if (!atomic_cas(&data->modem_usage, STATE_BUSY, STATE_CLEANUP)) {
89 return false;
90 }
91 /* Put radio back into sleep mode */
92 Radio.Sleep();
93 /* Completely release modem */
94 data->operation_done = NULL;
95 atomic_clear(&data->modem_usage);
96 return true;
97 }
98
sx12xx_ev_rx_done(uint8_t * payload,uint16_t size,int16_t rssi,int8_t snr)99 static void sx12xx_ev_rx_done(uint8_t *payload, uint16_t size, int16_t rssi,
100 int8_t snr)
101 {
102 struct k_poll_signal *sig = dev_data.operation_done;
103
104 /* Receiving in asynchronous mode */
105 if (dev_data.async_rx_cb) {
106 /* Start receiving again */
107 Radio.Rx(0);
108 /* Run the callback */
109 dev_data.async_rx_cb(dev_data.dev, payload, size, rssi, snr);
110 /* Don't run the synchronous code */
111 return;
112 }
113
114 /* Manually release the modem instead of just calling modem_release
115 * as we need to perform cleanup operations while still ensuring
116 * others can't use the modem.
117 */
118 if (!atomic_cas(&dev_data.modem_usage, STATE_BUSY, STATE_CLEANUP)) {
119 return;
120 }
121 /* We can make two observations here:
122 * 1. lora_recv hasn't already exited due to a timeout.
123 * (modem_release would have been successfully called)
124 * 2. If the k_poll in lora_recv times out before we raise the signal,
125 * but while this code is running, it will block on the
126 * signal again.
127 * This lets us guarantee that the operation_done signal and pointers
128 * in rx_params are always valid in this function.
129 */
130
131 /* Store actual size */
132 if (size < *dev_data.rx_params.size) {
133 *dev_data.rx_params.size = size;
134 }
135 /* Copy received data to output buffer */
136 memcpy(dev_data.rx_params.buf, payload,
137 *dev_data.rx_params.size);
138 /* Output RSSI and SNR */
139 if (dev_data.rx_params.rssi) {
140 *dev_data.rx_params.rssi = rssi;
141 }
142 if (dev_data.rx_params.snr) {
143 *dev_data.rx_params.snr = snr;
144 }
145 /* Put radio back into sleep mode */
146 Radio.Sleep();
147 /* Completely release modem */
148 dev_data.operation_done = NULL;
149 atomic_clear(&dev_data.modem_usage);
150 /* Notify caller RX is complete */
151 k_poll_signal_raise(sig, 0);
152 }
153
sx12xx_ev_tx_done(void)154 static void sx12xx_ev_tx_done(void)
155 {
156 struct k_poll_signal *sig = dev_data.operation_done;
157
158 if (modem_release(&dev_data)) {
159 /* Raise signal if provided */
160 if (sig) {
161 k_poll_signal_raise(sig, 0);
162 }
163 }
164 }
165
sx12xx_ev_tx_timed_out(void)166 static void sx12xx_ev_tx_timed_out(void)
167 {
168 /* Just release the modem */
169 modem_release(&dev_data);
170 }
171
sx12xx_lora_send(const struct device * dev,uint8_t * data,uint32_t data_len)172 int sx12xx_lora_send(const struct device *dev, uint8_t *data,
173 uint32_t data_len)
174 {
175 struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
176 struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
177 K_POLL_TYPE_SIGNAL,
178 K_POLL_MODE_NOTIFY_ONLY,
179 &done);
180 uint32_t air_time;
181 int ret;
182
183 /* Validate that we have a TX configuration */
184 if (!dev_data.tx_cfg.frequency) {
185 return -EINVAL;
186 }
187
188 ret = sx12xx_lora_send_async(dev, data, data_len, &done);
189 if (ret < 0) {
190 return ret;
191 }
192
193 /* Calculate expected airtime of the packet */
194 air_time = Radio.TimeOnAir(MODEM_LORA,
195 dev_data.tx_cfg.bandwidth,
196 dev_data.tx_cfg.datarate,
197 dev_data.tx_cfg.coding_rate,
198 dev_data.tx_cfg.preamble_len,
199 0, data_len, true);
200 LOG_DBG("Expected air time of %d bytes = %dms", data_len, air_time);
201
202 /* Wait for the packet to finish transmitting.
203 * Use twice the tx duration to ensure that we are actually detecting
204 * a failed transmission, and not some minor timing variation between
205 * modem and driver.
206 */
207 ret = k_poll(&evt, 1, K_MSEC(2 * air_time));
208 if (ret < 0) {
209 LOG_ERR("Packet transmission failed!");
210 if (!modem_release(&dev_data)) {
211 /* TX done interrupt is currently running */
212 k_poll(&evt, 1, K_FOREVER);
213 }
214 }
215 return ret;
216 }
217
sx12xx_lora_send_async(const struct device * dev,uint8_t * data,uint32_t data_len,struct k_poll_signal * async)218 int sx12xx_lora_send_async(const struct device *dev, uint8_t *data,
219 uint32_t data_len, struct k_poll_signal *async)
220 {
221 /* Ensure available, freed by sx12xx_ev_tx_done */
222 if (!modem_acquire(&dev_data)) {
223 return -EBUSY;
224 }
225
226 /* Store signal */
227 dev_data.operation_done = async;
228
229 Radio.SetMaxPayloadLength(MODEM_LORA, data_len);
230
231 Radio.Send(data, data_len);
232
233 return 0;
234 }
235
sx12xx_lora_recv(const struct device * dev,uint8_t * data,uint8_t size,k_timeout_t timeout,int16_t * rssi,int8_t * snr)236 int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
237 k_timeout_t timeout, int16_t *rssi, int8_t *snr)
238 {
239 struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
240 struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
241 K_POLL_TYPE_SIGNAL,
242 K_POLL_MODE_NOTIFY_ONLY,
243 &done);
244 int ret;
245
246 /* Ensure available, decremented by sx12xx_ev_rx_done or on timeout */
247 if (!modem_acquire(&dev_data)) {
248 return -EBUSY;
249 }
250
251 dev_data.async_rx_cb = NULL;
252 /* Store operation signal */
253 dev_data.operation_done = &done;
254 /* Set data output location */
255 dev_data.rx_params.buf = data;
256 dev_data.rx_params.size = &size;
257 dev_data.rx_params.rssi = rssi;
258 dev_data.rx_params.snr = snr;
259
260 Radio.SetMaxPayloadLength(MODEM_LORA, 255);
261 Radio.Rx(0);
262
263 ret = k_poll(&evt, 1, timeout);
264 if (ret < 0) {
265 if (!modem_release(&dev_data)) {
266 /* Releasing the modem failed, which means that
267 * the RX callback is currently running. Wait until
268 * the RX callback finishes and we get our packet.
269 */
270 k_poll(&evt, 1, K_FOREVER);
271
272 /* We did receive a packet */
273 return size;
274 }
275 LOG_INF("Receive timeout");
276 return ret;
277 }
278
279 return size;
280 }
281
sx12xx_lora_recv_async(const struct device * dev,lora_recv_cb cb)282 int sx12xx_lora_recv_async(const struct device *dev, lora_recv_cb cb)
283 {
284 /* Cancel ongoing reception */
285 if (cb == NULL) {
286 if (!modem_release(&dev_data)) {
287 /* Not receiving or already being stopped */
288 return -EINVAL;
289 }
290 return 0;
291 }
292
293 /* Ensure available */
294 if (!modem_acquire(&dev_data)) {
295 return -EBUSY;
296 }
297
298 /* Store parameters */
299 dev_data.async_rx_cb = cb;
300
301 /* Start reception */
302 Radio.SetMaxPayloadLength(MODEM_LORA, 255);
303 Radio.Rx(0);
304
305 return 0;
306 }
307
sx12xx_lora_config(const struct device * dev,struct lora_modem_config * config)308 int sx12xx_lora_config(const struct device *dev,
309 struct lora_modem_config *config)
310 {
311 /* Ensure available, decremented after configuration */
312 if (!modem_acquire(&dev_data)) {
313 return -EBUSY;
314 }
315
316 Radio.SetChannel(config->frequency);
317
318 if (config->tx) {
319 /* Store TX config locally for airtime calculations */
320 memcpy(&dev_data.tx_cfg, config, sizeof(dev_data.tx_cfg));
321 /* Configure radio driver */
322 Radio.SetTxConfig(MODEM_LORA, config->tx_power, 0,
323 config->bandwidth, config->datarate,
324 config->coding_rate, config->preamble_len,
325 false, true, 0, 0, config->iq_inverted, 4000);
326 } else {
327 /* TODO: Get symbol timeout value from config parameters */
328 Radio.SetRxConfig(MODEM_LORA, config->bandwidth,
329 config->datarate, config->coding_rate,
330 0, config->preamble_len, 10, false, 0,
331 false, 0, 0, config->iq_inverted, true);
332 }
333
334 Radio.SetPublicNetwork(config->public_network);
335
336 modem_release(&dev_data);
337 return 0;
338 }
339
sx12xx_lora_test_cw(const struct device * dev,uint32_t frequency,int8_t tx_power,uint16_t duration)340 int sx12xx_lora_test_cw(const struct device *dev, uint32_t frequency,
341 int8_t tx_power,
342 uint16_t duration)
343 {
344 /* Ensure available, freed in sx12xx_ev_tx_done */
345 if (!modem_acquire(&dev_data)) {
346 return -EBUSY;
347 }
348
349 Radio.SetTxContinuousWave(frequency, tx_power, duration);
350 return 0;
351 }
352
sx12xx_init(const struct device * dev)353 int sx12xx_init(const struct device *dev)
354 {
355 atomic_set(&dev_data.modem_usage, 0);
356
357 dev_data.dev = dev;
358 dev_data.events.TxDone = sx12xx_ev_tx_done;
359 dev_data.events.RxDone = sx12xx_ev_rx_done;
360 /* TX timeout event raises at the end of the test CW transmission */
361 dev_data.events.TxTimeout = sx12xx_ev_tx_timed_out;
362 Radio.Init(&dev_data.events);
363
364 /*
365 * Automatically place the radio into sleep mode upon boot.
366 * The required `lora_config` call before transmission or reception
367 * will bring the radio out of sleep mode before it is used. The radio
368 * is automatically placed back into sleep mode upon TX or RX
369 * completion.
370 */
371 Radio.Sleep();
372
373 return 0;
374 }
375