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