libsi24: add dynamic payload support
[libsi24] / libsi24.c
1 /**
2  * File              : libsi24.c
3  * Author            : Robin Krens <robin@robinkrens.nl>
4  * Date              : 18.01.2023
5  * Last Modified Date: 22.01.2023
6  * Last Modified By  : Robin Krens <robin@robinkrens.nl>
7  */
8
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "libsi24.h"
16 #include "libsi24reg.h"
17
18 #define DEBUG 0
19 #define TIMEOUT 0xFFFF
20
21 struct si24_t {
22         const si24_opts_t *opts;
23         const si24_ioctl_t *ctl;
24         si24_event_handler_t eh;
25 };
26
27 static uint8_t _reg_read(si24_t *si, uint8_t reg,
28                 uint8_t *data, int sz)
29 {
30         uint8_t buf[sz+1];
31         
32         memset(buf, 0, sz+1); 
33         buf[0] = reg | SI24_R_REGISTER;
34
35         
36         if (si->ctl->write_and_read(buf, sz+1) == -1) {
37                 si24_event_t ev;
38                 ev.type = EV_ERR_BUS;
39                 si->eh(si, &ev);
40                 return -1;
41         }
42         
43         memcpy(data, (buf+1), sz);
44         
45         return 0;
46 }
47
48 static uint8_t _reg_write(si24_t *si, uint8_t reg,
49                 const uint8_t *data, int sz)
50 {
51         uint8_t buf[sz+1];
52         
53         buf[0] = reg | SI24_W_REGISTER;
54         memcpy((buf+1), data, sz);
55         
56         if(DEBUG) {
57                 printf("REG 0x%x:\t", reg);
58                 for (int i = 1; i <= sz; ++i) {
59                         fprintf(stdout, "0x%x(%c)", buf[i], buf[i]);
60                 }
61                 fprintf(stdout, "\n");
62         }
63
64         if (si->ctl->write_and_read(buf, sz+1) == -1) {
65                 si24_event_t ev;
66                 ev.type = EV_ERR_BUS;
67                 si->eh(si, &ev);
68                 return -1;
69         }
70
71         return 0;
72 }
73
74 static int _config(si24_t * si)
75 {
76         int ret = 0;
77         uint8_t config_reg = (1 << PWR_UP);
78         uint8_t feature_reg = 0x0; /* default value */
79         uint8_t rf_setup_reg = 0xE; /* default value */
80         uint8_t setup_retr_reg = 0x3; /* default value */
81         const uint8_t rf_ch_reg = 0x40; /* default value */
82         const si24_opts_t * params = si->opts; 
83         
84         if (params->enable_crc) {
85                 config_reg |= (1 << EN_CRC);
86                 config_reg |= (si->opts->crc << CRCO);
87         }
88
89         if (params->enable_ack) {
90                 if (params->mode == SEND_MODE) {
91                         setup_retr_reg = ARD(params->timeout) | ARC(params->retries);
92                         ret += _reg_write(si, SI24_REG_SETUP_RETR, &setup_retr_reg, 1);
93                 }
94         } else {
95                 if (params->mode == SEND_MODE) {
96                         feature_reg |= (1 << EN_DYN_ACK);
97                         ret += _reg_write(si, SI24_REG_FEATURE, &feature_reg, 1);
98                 }
99         }
100
101         if (params->enable_dynpd) {
102                 uint8_t dyn = (1 << DPL_P0);
103                 ret += _reg_write(si, SI24_REG_DYNPD, &dyn, 1);
104                 feature_reg |= (1 << EN_DPL);
105                 ret += _reg_write(si, SI24_REG_FEATURE, &feature_reg, 1);
106         } else { /* fixed payload size */
107                 if (params->mode == RECV_MODE) {
108                         ret += _reg_write(si, SI24_REG_RX_PW_P0, (uint8_t *) &params->payload, 1);
109                 }
110         }
111
112         uint8_t aw = AW_5;
113         ret += _reg_write(si, SI24_REG_SETUP_AW, &aw, 1); 
114
115         if (params->mode == SEND_MODE && params->enable_ack) {
116                 ret += _reg_write(si, SI24_REG_RX_ADDR_P0, params->mac_addr, sizeof(params->mac_addr));
117         }
118         
119         if (params->mode == RECV_MODE) {
120                 config_reg |= (1 << PRIM_RX);
121                 uint8_t ch = 0x1;
122                 ret += _reg_write(si, SI24_REG_EN_RXADDR, &ch, 1); 
123                 ret += _reg_write(si, SI24_REG_RX_ADDR_P0, params->mac_addr, sizeof(params->mac_addr));
124         } else {
125                 ret += _reg_write(si, SI24_REG_TX_ADDR, params->mac_addr, sizeof(params->mac_addr));
126         }
127
128         rf_setup_reg |= (params->speed << RF_DR_HIGH);
129         rf_setup_reg |= (params->txpwr << RF_PWR);
130         ret += _reg_write(si, SI24_REG_RF_SETUP, &rf_setup_reg, 1);
131
132
133         ret += _reg_write(si, SI24_REG_RF_CH, &rf_ch_reg, 1);
134         ret += _reg_write(si, SI24_REG_CONFIG, &config_reg, 1);
135
136         if (params->mode == RECV_MODE) {
137                 /* start accepting data immediately,
138                  * for send mode it is onyl activated upon sending */
139                 params->ioctl->chip_enable(1);
140         }
141
142         return ret;
143 }
144
145 si24_t* si24_init(const si24_opts_t *opts, si24_event_handler_t eh)
146 {
147         struct si24_t *si = (si24_t*) calloc(1, sizeof(si24_t));
148         if (si == 0)
149                 return 0;
150
151         si->opts = opts;
152         si->ctl = opts->ioctl;
153         si->eh = eh;
154
155         int ret = _config(si);
156         if (ret < 0) {
157                 free(si);
158                 return 0;
159         }
160
161         return si;
162 }
163
164 size_t si24_send(si24_t* si, const unsigned char * buf, size_t size)
165 {
166         si24_event_t ev;
167         size_t bytes_sent = 0;
168         uint16_t timeout = 0;
169         int sz;
170         uint8_t flags;
171         
172         if (si->opts->mode == RECV_MODE)
173                 return -1;
174
175         _reg_read(si, SI24_REG_STATUS, (uint8_t *) &flags, 1);
176
177         if (flags & (1 << TX_FULL)) {
178                 ev.type = EV_TX_FULL;
179                 si->eh(si, &ev);
180                 return -1;
181         }
182
183         int payload = si->opts->payload;
184
185         if (si->opts->enable_dynpd)
186                 payload = size > 32 ? 32 : size;
187
188         for (size_t idx = 0; idx < size; idx += payload) {
189                 sz = (size - idx) < payload ? (size - idx) : payload;  
190                 if (si->opts->enable_ack) {
191                         _reg_write(si, SI24_W_TX_PAYLOAD, buf + idx, sz);
192                         si->ctl->chip_enable(1);
193                         while ((!(flags & (1 << TX_DS)) && !(flags & (1 << MAX_RT))) && timeout < TIMEOUT) {
194                                 _reg_read(si, SI24_REG_STATUS, &flags, 1);
195                                 timeout++;
196                         }
197                         if (flags & (1 << MAX_RT)) {
198                                 ev.type = EV_ERR_MAX_RETRIES;
199                                 si->eh(si, &ev);
200                                 si24_reset(si);
201                                 return bytes_sent;
202                         }
203
204                 } else {
205                         _reg_write(si, SI24_W_TX_PAYLOAD_NO_ACK, buf + idx, sz);
206                         si->ctl->chip_enable(1);
207                         while (!(flags & (1 << TX_DS)) && timeout < TIMEOUT) {
208                                 _reg_read(si, SI24_REG_STATUS, &flags, 1);
209                                 timeout++;
210                         }
211                 }
212
213                 if (timeout >= TIMEOUT) {
214                         ev.type = EV_ERR_TIMEOUT;
215                         si->eh(si, &ev);
216                         si24_reset(si);
217                         return bytes_sent;
218                 }
219
220                 flags |= (1 << TX_DS);
221                 _reg_write(si, SI24_REG_STATUS, &flags, 1);
222                 _reg_read(si, SI24_REG_STATUS, &flags, 1);
223                 bytes_sent += sz;
224                 timeout = 0;
225         }
226
227         ev.type = EV_TX_COMPLETE;
228         si->eh(si, &ev);
229         si->ctl->chip_enable(0);
230
231         return bytes_sent;
232 }
233
234 size_t si24_recv(si24_t* si, unsigned char * buf, size_t size) 
235 {
236         si24_event_t ev;
237         size_t bytes_read = 0;
238         uint8_t p_size = si->opts->payload;
239         uint8_t tmpbuf[p_size];
240         uint8_t flags;
241         uint8_t fifo_flags;
242         
243         if (si->opts->mode == SEND_MODE)
244                 return -1;
245
246         _reg_read(si, SI24_REG_STATUS, &flags, 1);
247
248         if (!(flags & (1 << RX_DR))) {
249                 ev.type = EV_RX_EMPTY;
250                 si->eh(si, &ev);
251                 return bytes_read;
252         }
253
254         /* do not accept any new incoming data */
255         si->opts->ioctl->chip_enable(0);
256
257         _reg_read(si, SI24_REG_FIFO_SATUS, &fifo_flags, 1);
258         while(!(fifo_flags & (1 << RX_EMPTY)) &&
259                         bytes_read < size) {
260                 
261                 if (si->opts->enable_dynpd) {
262                         uint8_t d_sz = 0;
263                         _reg_read(si, SI24_RX_PL_WID, &d_sz, 1);
264                         p_size = d_sz;
265                 }
266                 int m_size = (size - bytes_read) > p_size ? p_size : (size - bytes_read);
267                 _reg_read(si, SI24_R_RX_PAYLOAD, tmpbuf, m_size);
268
269                 memcpy(buf + bytes_read, tmpbuf, m_size);
270                 bytes_read += m_size;
271
272                 _reg_read(si, SI24_REG_FIFO_SATUS, &fifo_flags, 1);
273         }
274
275         /* only clear data ready flag when FIFO is empty */
276         if (fifo_flags & (1 << RX_EMPTY)) {
277                 flags |= (1 << RX_DR);
278                 _reg_write(si, SI24_REG_STATUS, &flags, 1);
279         }
280         
281         ev.type = EV_RX_COMPLETE;
282         si->eh(si, &ev);
283
284         si->opts->ioctl->chip_enable(1);
285         
286         return bytes_read;
287 }
288
289 void si24_reset(si24_t* si)
290 {
291         if (si->opts->mode == RECV_MODE) {
292                 _reg_write(si, SI24_FLUSH_RX, 0, 0);
293         }
294         else if (si->opts->mode == SEND_MODE) {
295                 _reg_write(si, SI24_FLUSH_TX, 0, 0);
296         }
297
298         uint8_t status_reg = {0};
299         status_reg |= (1 << RX_DR);
300         status_reg |= (1 << TX_DS);
301         status_reg |= (1 << MAX_RT);
302
303         _reg_write(si, SI24_REG_STATUS, (uint8_t *) &status_reg, 1);
304
305         si->ctl->chip_enable(0);
306 }
307
308 void si24_free(si24_t * si)
309 {
310         free(si);
311 }