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