ca5fcb464d9129508098394870b2568bdb724be9
[fw/altos] / src / kernel / ao_storage.c
1 /*
2  * Copyright © 2011 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 #include <ao.h>
20 #include <ao_storage.h>
21
22 #define AO_STORAGE_DATA_SIZE    128
23
24 static uint8_t storage_data[AO_STORAGE_DATA_SIZE];
25
26 uint8_t
27 ao_storage_read(ao_pos_t pos, void *v_buf, uint16_t len)
28 {
29         uint8_t *buf = v_buf;
30         uint16_t this_len;
31         uint16_t this_off;
32
33         ao_storage_setup();
34         if (pos >= ao_storage_total || pos + len > ao_storage_total)
35                 return 0;
36         while (len) {
37
38                 /* Compute portion of transfer within
39                  * a single block
40                  */
41                 this_off = (uint16_t) pos & (ao_storage_unit - 1);
42                 this_len = ao_storage_unit - this_off;
43                 if (this_len > len)
44                         this_len = len;
45
46                 if (!ao_storage_device_read(pos, buf, this_len))
47                         return 0;
48
49                 /* See how much is left */
50                 buf += this_len;
51                 len -= this_len;
52                 pos += this_len;
53         }
54         return 1;
55 }
56
57 uint8_t
58 ao_storage_write(ao_pos_t pos, void *v_buf, uint16_t len) 
59 {
60         uint8_t *buf = v_buf;
61         uint16_t this_len;
62         uint16_t this_off;
63
64         ao_storage_setup();
65         if (pos >= ao_storage_total || pos + len > ao_storage_total)
66                 return 0;
67         while (len) {
68
69                 /* Compute portion of transfer within
70                  * a single block
71                  */
72                 this_off = (uint16_t) pos & (ao_storage_unit - 1);
73                 this_len = ao_storage_unit - this_off;
74                 if (this_len > len)
75                         this_len = len;
76
77                 if (!ao_storage_device_write(pos, buf, this_len))
78                         return 0;
79
80                 /* See how much is left */
81                 buf += this_len;
82                 len -= this_len;
83                 pos += this_len;
84         }
85         return 1;
86 }
87
88 uint8_t
89 ao_storage_is_erased(uint32_t pos)
90 {
91         uint32_t        read_pos;
92         uint32_t        read_len;
93         uint32_t        i;
94
95         read_pos = pos;
96         read_len = ao_storage_block;
97         while (read_len) {
98                 uint32_t this_time = AO_STORAGE_DATA_SIZE;
99                 if (this_time > read_len)
100                         this_time = read_len;
101                 if (!ao_storage_read(read_pos, storage_data, this_time))
102                         return 0;
103                 for (i = 0; i < this_time; i++)
104                         if (storage_data[i] != 0xff)
105                                 return 0;
106                 read_pos += this_time;
107                 read_len -= this_time;
108         }
109         return 1;
110 }
111
112 uint8_t
113 ao_storage_erase(uint32_t start_pos, uint32_t len)
114 {
115         /* Round 'len' up to ao_storage_block units */
116         len = ((len + ao_storage_block - 1) / ao_storage_block) * ao_storage_block;
117
118         /*
119          * Start at the end of the area to erase so that the
120          * last block cleared is the first block; this will ensure
121          * that partially erased flight logs still appear in the list
122          * and can be re-erased.
123          */
124         uint32_t pos = start_pos + len - ao_storage_block;
125         while (len) {
126                 int tries;
127
128 #define MAX_TRIES       4       /* needs to be at least 2 */
129                 for (tries = 0; tries < MAX_TRIES; tries++) {
130                         if (ao_storage_is_erased(pos))
131                                 break;
132                         if (!ao_storage_device_erase(pos))
133                                 return 0;
134                 }
135                 if (tries == MAX_TRIES)
136                         return 0;
137                 pos -= ao_storage_block;
138                 len -= ao_storage_block;
139         }
140         return 1;
141 }
142
143 static void
144 ao_storage_dump(void) 
145 {
146         uint32_t block;
147         uint8_t i, j;
148
149         block = ao_cmd_hex();
150         if (ao_cmd_status != ao_cmd_success)
151                 return;
152         for (i = 0; ; i += 8) {
153                 if (ao_storage_read((block << 8) + i,
154                                     storage_data,
155                                     8)) {
156                         ao_cmd_put16((uint16_t) i);
157                         for (j = 0; j < 8; j++) {
158                                 putchar(' ');
159                                 ao_cmd_put8(storage_data[j]);
160                         }
161                         putchar ('\n');
162                 }
163                 if (i == 248)
164                         break;
165         }
166 }
167
168 #if HAS_STORAGE_DEBUG
169
170 /* not enough space for this today
171  */
172 static void
173 ao_storage_store(void) 
174 {
175         uint16_t block;
176         uint8_t i;
177         uint16_t len;
178         uint8_t b;
179         uint32_t addr;
180
181         block = ao_cmd_hex();
182         i = ao_cmd_hex();
183         addr = ((uint32_t) block << 8) | i;
184         len = ao_cmd_hex();
185         if (ao_cmd_status != ao_cmd_success)
186                 return;
187         while (len--) {
188                 b = ao_cmd_hexbyte();
189                 if (ao_cmd_status != ao_cmd_success)
190                         return;
191                 ao_storage_write(addr, &b, 1);
192                 addr++;
193         }
194 }
195 #endif
196
197 static void
198 ao_storage_zap(void) 
199 {
200         uint32_t v = ao_cmd_hex();
201         if (ao_cmd_status != ao_cmd_success)
202                 return;
203         ao_storage_erase((uint32_t) v << 8, ao_storage_block);
204 }
205
206 static void
207 ao_storage_zapall(void) 
208 {
209         ao_cmd_white();
210         if (!ao_match_word("DoIt"))
211                 return;
212         ao_storage_erase(0, ao_storage_log_max);
213 }
214
215 #if AO_STORAGE_TEST
216
217 #define AO_STORAGE_TEST_SIZE    256
218 static uint8_t storage_test[AO_STORAGE_TEST_SIZE];
219
220 static void
221 ao_storage_failure(uint32_t pos, char *format, ...)
222 {
223         va_list a;
224         printf("TEST FAILURE AT %08x: ", pos);
225         va_start(a, format);
226         vprintf(format, a);
227         va_end(a);
228 }
229
230 static uint8_t
231 ao_storage_check_block(uint32_t pos, uint8_t value)
232 {
233         uint32_t        offset;
234         uint32_t        byte;
235
236         for (offset = 0; offset < ao_storage_block; offset += sizeof (storage_test)) {
237                 if (!ao_storage_read(pos + offset, storage_test, sizeof (storage_test))) {
238                         ao_storage_failure(pos + offset, "read failed\n");
239                         return 0;
240                 }
241                 for (byte = 0; byte < sizeof (storage_test); byte++)
242                         if (storage_test[byte] != value) {
243                                 ao_storage_failure(pos + offset + byte,
244                                                    "want %02x got %02x\n",
245                                                    value, storage_test[byte]);
246                                 return 0;
247                         }
248         }
249         return 1;
250 }
251
252 static uint8_t
253 ao_storage_fill_block(uint32_t pos, uint8_t value)
254 {
255         uint32_t        offset;
256         uint32_t        byte;
257
258         for (byte = 0; byte < sizeof (storage_test); byte++)
259                 storage_test[byte] = value;
260         for (offset = 0; offset < ao_storage_block; offset += sizeof (storage_test)) {
261                 if (!ao_storage_write(pos + offset, storage_test, sizeof (storage_test))) {
262                         ao_storage_failure(pos + offset, "write failed\n");
263                         return 0;
264                 }
265         }
266         return 1;
267 }
268
269 static uint8_t
270 ao_storage_check_incr_block(uint32_t pos)
271 {
272         uint32_t        offset;
273         uint32_t        byte;
274
275         for (offset = 0; offset < ao_storage_block; offset += sizeof (storage_test)) {
276                 if (!ao_storage_read(pos + offset, storage_test, sizeof (storage_test))) {
277                         ao_storage_failure(pos + offset, "read failed\n");
278                         return 0;
279                 }
280                 for (byte = 0; byte < sizeof (storage_test); byte++) {
281                         uint8_t value = offset + byte;
282                         if (storage_test[byte] != value) {
283                                 ao_storage_failure(pos + offset + byte,
284                                                    "want %02x got %02x\n",
285                                                    value, storage_test[byte]);
286                                 return 0;
287                         }
288                 }
289         }
290         return 1;
291 }
292
293 static uint8_t
294 ao_storage_fill_incr_block(uint32_t pos)
295 {
296         uint32_t        offset;
297         uint32_t        byte;
298
299         for (offset = 0; offset < ao_storage_block; offset += sizeof (storage_test)) {
300                 for (byte = 0; byte < sizeof (storage_test); byte++)
301                         storage_test[byte] = offset + byte;
302                 if (!ao_storage_write(pos + offset, storage_test, sizeof (storage_test))) {
303                         ao_storage_failure(pos + offset, "write failed\n");
304                         return 0;
305                 }
306         }
307         return 1;
308 }
309
310 static uint8_t
311 ao_storage_fill_check_block(uint32_t pos, uint8_t value)
312 {
313         return ao_storage_fill_block(pos, value) && ao_storage_check_block(pos, value);
314 }
315
316 static uint8_t
317 ao_storage_incr_check_block(uint32_t pos)
318 {
319         return ao_storage_fill_incr_block(pos) && ao_storage_check_incr_block(pos);
320 }
321
322 static uint8_t
323 ao_storage_test_block(uint32_t pos) 
324 {
325         ao_storage_erase(pos, ao_storage_block);
326         printf(" erase"); flush();
327         if (!ao_storage_check_block(pos, 0xff))
328                 return 0;
329         printf(" zero"); flush();
330         if (!ao_storage_fill_check_block(pos, 0x00))
331                 return 0;
332         ao_storage_erase(pos, ao_storage_block);
333         printf(" 0xaa"); flush();
334         if (!ao_storage_fill_check_block(pos, 0xaa))
335                 return 0;
336         ao_storage_erase(pos, ao_storage_block);
337         printf(" 0x55"); flush();
338         if (!ao_storage_fill_check_block(pos, 0x55))
339                 return 0;
340         ao_storage_erase(pos, ao_storage_block);
341         printf(" increment"); flush();
342         if (!ao_storage_incr_check_block(pos))
343                 return 0;
344         ao_storage_erase(pos, ao_storage_block);
345         printf(" pass\n"); flush();
346         return 1;
347 }
348
349 static void
350 ao_storage_test(void) 
351 {
352         uint32_t        pos;
353
354         ao_cmd_white();
355         if (!ao_match_word("DoIt"))
356                 return;
357         for (pos = 0; pos < ao_storage_log_max; pos += ao_storage_block) {
358                 printf("Testing block 0x%08x:", pos); flush();
359                 if (!ao_storage_test_block(pos))
360                         break;
361         }
362         printf("Test complete\n");
363 }
364
365 static void
366 ao_storage_fill(void)
367 {
368         uint32_t        pos;
369
370         ao_cmd_white();
371         if (!ao_match_word("DoIt"))
372                 return;
373         printf("erase "); flush();
374         ao_storage_erase(0, ao_storage_log_max);
375         for (pos = 0; pos < sizeof (storage_test); pos++)
376                 storage_test[pos] = (uint8_t) pos;
377         for (pos = 0; pos < ao_storage_log_max; pos += sizeof (storage_test)) {
378                 if ((pos & 0xffff) == 0) {
379                         printf("Fill 0x%x\n", pos); flush();
380                 }
381                 ao_storage_write(pos, storage_test, sizeof (storage_test));
382         }
383         printf("Fill complete\n");
384 }
385 #endif /* AO_STORAGE_TEST */
386
387 static void
388 ao_storage_info(void) 
389 {
390         ao_storage_setup();
391         printf("Storage size: %ld\n", (long) ao_storage_total);
392         printf("Storage erase unit: %ld\n", (long) ao_storage_block);
393         ao_storage_device_info();
394 }
395
396 const struct ao_cmds ao_storage_cmds[] = {
397         { ao_storage_info, "f\0Show storage" },
398         { ao_storage_dump, "e <block>\0Dump flash" },
399 #if HAS_STORAGE_DEBUG
400         { ao_storage_store, "w <block> <start> <len> <data> ...\0Write data to flash" },
401 #endif
402         { ao_storage_zap, "z <block>\0Erase <block>" },
403         { ao_storage_zapall,"Z <key>\0Erase all. <key> is doit with D&I" },
404 #if AO_STORAGE_TEST
405         { ao_storage_test, "V <key>\0Validate flash (destructive). <key> is doit with D&I" },
406         { ao_storage_fill, "F <key>\0Fill flash with data. <key> is doit with D&I" },
407 #endif
408         { 0, NULL },
409 };
410
411 void
412 ao_storage_init(void)
413 {
414         ao_storage_device_init();
415         ao_cmd_register(&ao_storage_cmds[0]);
416 }