1 /*
2 * Copyright (c) 2019 Manivannan Sadhasivam
3 * Copyright (c) 2020 Grinn
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <drivers/gpio.h>
9 #include <drivers/lora.h>
10 #include <logging/log.h>
11 #include <sys/atomic.h>
12 #include <zephyr.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 struct k_poll_signal *operation_done;
34 RadioEvents_t events;
35 struct lora_modem_config tx_cfg;
36 atomic_t modem_usage;
37 struct sx12xx_rx_params rx_params;
38 } dev_data;
39
__sx12xx_configure_pin(const struct device ** dev,const char * controller,gpio_pin_t pin,gpio_flags_t flags)40 int __sx12xx_configure_pin(const struct device **dev, const char *controller,
41 gpio_pin_t pin, gpio_flags_t flags)
42 {
43 int err;
44
45 *dev = device_get_binding(controller);
46 if (!(*dev)) {
47 LOG_ERR("Cannot get pointer to %s device", controller);
48 return -EIO;
49 }
50
51 err = gpio_pin_configure(*dev, pin, flags);
52 if (err) {
53 LOG_ERR("Cannot configure gpio %s %d: %d", controller, pin,
54 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 /* Manually release the modem instead of just calling modem_release
105 * as we need to perform cleanup operations while still ensuring
106 * others can't use the modem.
107 */
108 if (!atomic_cas(&dev_data.modem_usage, STATE_BUSY, STATE_CLEANUP)) {
109 return;
110 }
111 /* We can make two observations here:
112 * 1. lora_recv hasn't already exited due to a timeout.
113 * (modem_release would have been successfully called)
114 * 2. If the k_poll in lora_recv times out before we raise the signal,
115 * but while this code is running, it will block on the
116 * signal again.
117 * This lets us guarantee that the operation_done signal and pointers
118 * in rx_params are always valid in this function.
119 */
120
121 /* Store actual size */
122 if (size < *dev_data.rx_params.size) {
123 *dev_data.rx_params.size = size;
124 }
125 /* Copy received data to output buffer */
126 memcpy(dev_data.rx_params.buf, payload,
127 *dev_data.rx_params.size);
128 /* Output RSSI and SNR */
129 if (dev_data.rx_params.rssi) {
130 *dev_data.rx_params.rssi = rssi;
131 }
132 if (dev_data.rx_params.snr) {
133 *dev_data.rx_params.snr = snr;
134 }
135 /* Put radio back into sleep mode */
136 Radio.Sleep();
137 /* Completely release modem */
138 dev_data.operation_done = NULL;
139 atomic_clear(&dev_data.modem_usage);
140 /* Notify caller RX is complete */
141 k_poll_signal_raise(sig, 0);
142 }
143
sx12xx_ev_tx_done(void)144 static void sx12xx_ev_tx_done(void)
145 {
146 struct k_poll_signal *sig = dev_data.operation_done;
147
148 if (modem_release(&dev_data)) {
149 /* Raise signal if provided */
150 if (sig) {
151 k_poll_signal_raise(sig, 0);
152 }
153 }
154 }
155
sx12xx_lora_send(const struct device * dev,uint8_t * data,uint32_t data_len)156 int sx12xx_lora_send(const struct device *dev, uint8_t *data,
157 uint32_t data_len)
158 {
159 struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
160 struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
161 K_POLL_TYPE_SIGNAL,
162 K_POLL_MODE_NOTIFY_ONLY,
163 &done);
164 uint32_t air_time;
165 int ret;
166
167 /* Validate that we have a TX configuration */
168 if (!dev_data.tx_cfg.frequency) {
169 return -EINVAL;
170 }
171
172 ret = sx12xx_lora_send_async(dev, data, data_len, &done);
173 if (ret < 0) {
174 return ret;
175 }
176
177 /* Calculate expected airtime of the packet */
178 air_time = Radio.TimeOnAir(MODEM_LORA,
179 dev_data.tx_cfg.bandwidth,
180 dev_data.tx_cfg.datarate,
181 dev_data.tx_cfg.coding_rate,
182 dev_data.tx_cfg.preamble_len,
183 0, data_len, true);
184 LOG_DBG("Expected air time of %d bytes = %dms", data_len, air_time);
185
186 /* Wait for the packet to finish transmitting.
187 * Use twice the tx duration to ensure that we are actually detecting
188 * a failed transmission, and not some minor timing variation between
189 * modem and driver.
190 */
191 ret = k_poll(&evt, 1, K_MSEC(2 * air_time));
192 if (ret < 0) {
193 LOG_ERR("Packet transmission failed!");
194 if (!modem_release(&dev_data)) {
195 /* TX done interrupt is currently running */
196 k_poll(&evt, 1, K_FOREVER);
197 }
198 }
199 return 0;
200 }
201
sx12xx_lora_send_async(const struct device * dev,uint8_t * data,uint32_t data_len,struct k_poll_signal * async)202 int sx12xx_lora_send_async(const struct device *dev, uint8_t *data,
203 uint32_t data_len, struct k_poll_signal *async)
204 {
205 /* Ensure available, freed by sx12xx_ev_tx_done */
206 if (!modem_acquire(&dev_data)) {
207 return -EBUSY;
208 }
209
210 /* Store signal */
211 dev_data.operation_done = async;
212
213 Radio.SetMaxPayloadLength(MODEM_LORA, data_len);
214
215 Radio.Send(data, data_len);
216
217 return 0;
218 }
219
sx12xx_lora_recv(const struct device * dev,uint8_t * data,uint8_t size,k_timeout_t timeout,int16_t * rssi,int8_t * snr)220 int sx12xx_lora_recv(const struct device *dev, uint8_t *data, uint8_t size,
221 k_timeout_t timeout, int16_t *rssi, int8_t *snr)
222 {
223 struct k_poll_signal done = K_POLL_SIGNAL_INITIALIZER(done);
224 struct k_poll_event evt = K_POLL_EVENT_INITIALIZER(
225 K_POLL_TYPE_SIGNAL,
226 K_POLL_MODE_NOTIFY_ONLY,
227 &done);
228 int ret;
229
230 /* Ensure available, decremented by sx12xx_ev_rx_done or on timeout */
231 if (!modem_acquire(&dev_data)) {
232 return -EBUSY;
233 }
234
235 /* Store operation signal */
236 dev_data.operation_done = &done;
237 /* Set data output location */
238 dev_data.rx_params.buf = data;
239 dev_data.rx_params.size = &size;
240 dev_data.rx_params.rssi = rssi;
241 dev_data.rx_params.snr = snr;
242
243 Radio.SetMaxPayloadLength(MODEM_LORA, 255);
244 Radio.Rx(0);
245
246 ret = k_poll(&evt, 1, timeout);
247 if (ret < 0) {
248 if (!modem_release(&dev_data)) {
249 /* Releasing the modem failed, which means that
250 * the RX callback is currently running. Wait until
251 * the RX callback finishes and we get our packet.
252 */
253 k_poll(&evt, 1, K_FOREVER);
254
255 /* We did receive a packet */
256 return size;
257 }
258 LOG_INF("Receive timeout");
259 return ret;
260 }
261
262 return size;
263 }
264
sx12xx_lora_config(const struct device * dev,struct lora_modem_config * config)265 int sx12xx_lora_config(const struct device *dev,
266 struct lora_modem_config *config)
267 {
268 /* Ensure available, decremented after configuration */
269 if (!modem_acquire(&dev_data)) {
270 return -EBUSY;
271 }
272
273 Radio.SetChannel(config->frequency);
274
275 if (config->tx) {
276 /* Store TX config locally for airtime calculations */
277 memcpy(&dev_data.tx_cfg, config, sizeof(dev_data.tx_cfg));
278 /* Configure radio driver */
279 Radio.SetTxConfig(MODEM_LORA, config->tx_power, 0,
280 config->bandwidth, config->datarate,
281 config->coding_rate, config->preamble_len,
282 false, true, 0, 0, false, 4000);
283 } else {
284 /* TODO: Get symbol timeout value from config parameters */
285 Radio.SetRxConfig(MODEM_LORA, config->bandwidth,
286 config->datarate, config->coding_rate,
287 0, config->preamble_len, 10, false, 0,
288 false, 0, 0, false, true);
289 }
290
291 modem_release(&dev_data);
292 return 0;
293 }
294
sx12xx_lora_test_cw(const struct device * dev,uint32_t frequency,int8_t tx_power,uint16_t duration)295 int sx12xx_lora_test_cw(const struct device *dev, uint32_t frequency,
296 int8_t tx_power,
297 uint16_t duration)
298 {
299 /* Ensure available, freed in sx12xx_ev_tx_done */
300 if (!modem_acquire(&dev_data)) {
301 return -EBUSY;
302 }
303
304 Radio.SetTxContinuousWave(frequency, tx_power, duration);
305 return 0;
306 }
307
sx12xx_init(const struct device * dev)308 int sx12xx_init(const struct device *dev)
309 {
310 atomic_set(&dev_data.modem_usage, 0);
311
312 dev_data.events.TxDone = sx12xx_ev_tx_done;
313 dev_data.events.RxDone = sx12xx_ev_rx_done;
314 Radio.Init(&dev_data.events);
315
316 /*
317 * Automatically place the radio into sleep mode upon boot.
318 * The required `lora_config` call before transmission or reception
319 * will bring the radio out of sleep mode before it is used. The radio
320 * is automatically placed back into sleep mode upon TX or RX
321 * completion.
322 */
323 Radio.Sleep();
324
325 return 0;
326 }
327