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