altos/cc1200: Adjust bit-sync configuration
[fw/altos] / src / drivers / ao_bufio.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #ifndef AO_FAT_TEST
19 #include "ao.h"
20 #endif
21
22 /* Include bufio commands */
23 #ifndef AO_FAT_TEST
24 #define BUFIO_COMMANDS  0
25 #endif
26
27 #include "ao_sdcard.h"
28 #include "ao_bufio.h"
29
30 #define AO_NUM_BUF              16
31 #define AO_BUFSIZ               512
32
33 struct ao_bufio {
34         uint32_t        block;
35         int16_t         seqno;
36         uint8_t         busy;   
37         uint8_t         dirty;
38 };
39
40 static struct ao_bufio  ao_bufio[AO_NUM_BUF];
41 static uint8_t          ao_buffer[AO_NUM_BUF][AO_BUFSIZ];
42 static int16_t          ao_seqno;
43 static uint8_t          ao_bufio_mutex;
44
45 #if 0
46 #define DBG(...) printf(__VA_ARGS__)
47 #else
48 #define DBG(...) (void) 0
49 #endif
50
51 static inline void
52 ao_bufio_lock(void)
53 {
54         ao_mutex_get(&ao_bufio_mutex);
55 }
56
57 static inline void
58 ao_bufio_unlock(void)
59 {
60         ao_mutex_put(&ao_bufio_mutex);
61 }
62
63 static inline int16_t
64 ao_seqno_age(int16_t s)
65 {
66         return ao_seqno - s;
67 }
68
69 static inline int16_t
70 ao_seqno_next(void)
71 {
72         return ++ao_seqno;
73 }
74
75 static inline int
76 ao_seqno_older(int16_t a, int16_t b)
77 {
78         return ao_seqno_age(a) > ao_seqno_age(b);
79 }
80
81 static inline void
82 ao_validate_bufno(int b)
83 {
84         if (b < 0 || AO_NUM_BUF <= b)
85                 ao_panic(AO_PANIC_BUFIO);
86 }
87
88 static inline int
89 ao_buf_to_num(uint8_t *buf)
90 {
91         int b = (buf - &ao_buffer[0][0]) / AO_BUFSIZ;
92
93         ao_validate_bufno(b);
94         return b;
95 }
96
97 static inline int
98 ao_bufio_to_num(struct ao_bufio *bufio)
99 {
100         int b = (bufio - ao_bufio);
101
102         ao_validate_bufno(b);
103         return b;
104 }
105
106 static inline struct ao_bufio *
107 ao_buf_to_bufio(uint8_t *buf)
108 {
109         int b = ao_buf_to_num(buf);
110         struct ao_bufio *bufio;
111
112         bufio = &ao_bufio[b];
113         DBG ("buf %08x is %d bufio %08x\n", buf, b, bufio);
114         return bufio;
115 }
116
117 static inline uint8_t *
118 ao_bufio_to_buf(struct ao_bufio *bufio)
119 {
120         int b = ao_bufio_to_num(bufio);
121         uint8_t *buf;
122
123         buf = &ao_buffer[b][0];
124         DBG ("bufio %08x is %d buf %08x\n", bufio, b, buf);
125         return buf;
126 }
127
128 /*
129  * Write a buffer to storage if it is dirty
130  */
131 static void
132 ao_bufio_write(struct ao_bufio *bufio)
133 {
134         if (bufio->dirty) {
135                 ao_sdcard_write_block(bufio->block, ao_bufio_to_buf(bufio));
136                 bufio->dirty = 0;
137         }
138 }
139
140 /*
141  * Read a buffer from storage
142  */
143 static uint8_t
144 ao_bufio_read(struct ao_bufio *bufio)
145 {
146         uint8_t *buf = ao_bufio_to_buf(bufio);
147
148         return ao_sdcard_read_block(bufio->block, buf);
149 }
150
151 /*
152  * Find a buffer containing the specified block
153  */
154 static struct ao_bufio *
155 ao_bufio_find_block(uint32_t block)
156 {
157         int b;
158
159         for (b = 0; b < AO_NUM_BUF; b++) {
160                 struct ao_bufio *bufio = &ao_bufio[b];
161                 if (bufio->block == block) {
162                         DBG ("Found existing buffer %d (seqno %d)\n",
163                                 ao_bufio_to_num(bufio), bufio->seqno);
164                         return bufio;
165                 }
166         }
167         return NULL;
168 }
169
170 /*
171  * Find the least recently used idle buffer
172  */
173 static struct ao_bufio *
174 ao_bufio_find_idle(void)
175 {
176         int b;
177         struct ao_bufio *oldest = NULL;
178
179         for (b = 0; b < AO_NUM_BUF; b++) {
180                 struct ao_bufio *bufio = &ao_bufio[b];
181                 if (!bufio->busy)
182                         if (!oldest || ao_seqno_older(bufio->seqno, oldest->seqno))
183                                 oldest = bufio;
184         }
185         if (oldest)
186                 DBG ("Using idle buffer %d (seqno %d)\n",
187                         ao_bufio_to_num(oldest), oldest->seqno);
188         return oldest;
189 }
190
191 /*
192  * Return a pointer to a buffer containing
193  * the contents of the specified block
194  */
195 uint8_t *
196 ao_bufio_get(uint32_t block)
197 {
198         struct ao_bufio *bufio;
199         uint8_t *buf = NULL;
200
201         ao_bufio_lock();
202         bufio = ao_bufio_find_block(block);
203         if (!bufio) {
204                 bufio = ao_bufio_find_idle();
205                 if (bufio) {
206                         ao_bufio_write(bufio);
207                         bufio->block = block;
208                         DBG ("read buffer\n");
209                         if (!ao_bufio_read(bufio)) {
210                                 bufio->block = 0xffffffff;
211                                 bufio = NULL;
212                         }
213                 } else
214                         ao_panic(AO_PANIC_BUFIO);
215         }
216         if (bufio) {
217                 bufio->busy++;
218                 if (!bufio->busy)
219                         ao_panic(AO_PANIC_BUFIO);
220                 buf = ao_bufio_to_buf(bufio);
221         }
222         ao_bufio_unlock();
223         return buf;
224 }
225
226 /*
227  * Release a buffer, marking it dirty
228  * if it has been written to
229  */
230 void
231 ao_bufio_put(uint8_t *buf, uint8_t write)
232 {
233         struct ao_bufio *bufio;
234
235         ao_bufio_lock();
236         bufio = ao_buf_to_bufio(buf);
237         
238         if (!bufio->busy)
239                 ao_panic(AO_PANIC_BUFIO);
240
241         DBG ("idle buffer %d write %d\n", ao_bufio_to_num(bufio), write);
242         bufio->dirty |= write;
243         if (!--bufio->busy) {
244                 bufio->seqno = ao_seqno_next();
245                 DBG ("not busy, seqno %d\n", bufio->seqno);
246         }
247         ao_bufio_unlock();
248 }
249
250 /*
251  * Flush a single buffer immediately. Useful
252  * if write order is important
253  */
254 void
255 ao_bufio_flush_one(uint8_t *buf)
256 {
257         ao_bufio_lock();
258         ao_bufio_write(ao_buf_to_bufio(buf));
259         ao_bufio_unlock();
260 }
261
262 /*
263  * Flush all buffers to storage
264  */
265 void
266 ao_bufio_flush(void)
267 {
268         int     b;
269
270         ao_bufio_lock();
271         for (b = 0; b < AO_NUM_BUF; b++)
272                 ao_bufio_write(&ao_bufio[b]);
273         ao_bufio_unlock();
274 }
275
276 #if BUFIO_COMMANDS
277 static void
278 ao_bufio_test_read(void)
279 {
280         uint8_t *buf;
281         ao_cmd_decimal();
282         if (ao_cmd_status != ao_cmd_success)
283                 return;
284         if ((buf = ao_bufio_get(ao_cmd_lex_u32))) {
285                 int i;
286                 for (i = 0; i < 512; i++) {
287                         printf (" %02x", buf[i]);
288                         if ((i & 0xf) == 0xf)
289                                 printf("\n");
290                 }
291                 ao_bufio_put(buf, 0);
292         }
293 }
294
295 static const struct ao_cmds ao_bufio_cmds[] = {
296         { ao_bufio_test_read,   "q\0Test bufio read" },
297         { 0, NULL },
298 };
299 #endif
300
301 void
302 ao_bufio_setup(void)
303 {
304         int b;
305
306         for (b = 0; b < AO_NUM_BUF; b++) {
307                 ao_bufio[b].dirty = 0;
308                 ao_bufio[b].busy = 0;
309                 ao_bufio[b].block = 0xffffffff;
310         }
311 }
312
313 void
314 ao_bufio_init(void)
315 {
316         ao_bufio_setup();
317         ao_sdcard_init();
318 #if BUFIO_COMMANDS
319         ao_cmd_register(&ao_bufio_cmds[0]);
320 #endif
321 }