Fix some doc strings; Add whence parameter to seek; rename module to flash_avr_;...
[bertos.git] / drv / flash_avr.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
5  * This file is part of DevLib - See README.devlib for information.
6  * -->
7  *
8  * \brief Self programming routines.
9  *
10  * \version $Id$
11  * \author Francesco Sacchi <batt@develer.com>
12  * \author Daniele Basile <asterix@develer.com>
13  *
14  * This module implements a kfile-like access for Atmel avr
15  * internal flash.
16  * Internal flash writing access is controlled by BOOTSZ fuses, check
17  * datasheet for details.
18  */
19
20 #include "flash_avr.h"
21
22 #include <avr/io.h>
23 #include <avr/boot.h>
24 #include <avr/pgmspace.h>
25
26 #include <cfg/macros.h> // MIN()
27 #include <cfg/compiler.h>
28 #include <cfg/debug.h>
29 #include <cfg/cpu.h>
30
31 #include <drv/wdt.h>
32
33 #include <string.h>
34 #include <stdio.h>
35
36 typedef uint16_t avr_page_addr_t;
37 typedef uint16_t avr_page_t;
38
39 /**
40  * Temporary buffer cointaing data block to
41  * write on flash.
42  */
43 static uint8_t page_buf[SPM_PAGESIZE];
44
45 bool page_modified; /// Flag for checking if current page is modified.
46
47 /**
48  * Current buffered page.
49  */
50 static avr_page_t curr_page = 0;
51
52 /**
53  * Write current buffered page in flash memory (if modified).
54  * This function erase flash memory page before writing.
55  */
56 static void flash_avr_flush(void)
57 {
58         if (page_modified)
59         {
60                 kprintf("Flushing page %d\n", curr_page);
61
62                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
63
64                 kprintf("Filling temparary page buffer...");
65                 /* Fill the temporary buffer of the AVR */
66                 for (avr_page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
67                 {
68                         uint16_t word = ((uint16_t)page_buf[page_addr + 1] << 8) | page_buf[page_addr];
69
70                         ATOMIC(boot_page_fill(page_addr, word));
71                 }
72                 kprintf("Done.\n");
73
74                 wdt_reset();
75
76                 kprintf("Erasing page, addr %u...", curr_page * SPM_PAGESIZE);
77
78                 /* Page erase */
79                 ATOMIC(boot_page_erase(curr_page * SPM_PAGESIZE));
80
81                 /* Wait until the memory is erased. */
82                 boot_spm_busy_wait();
83
84                 kprintf("Done.\n");
85                 kprintf("Writing page, addr %u...", curr_page * SPM_PAGESIZE);
86
87                 /* Store buffer in flash page. */
88                 ATOMIC(boot_page_write(curr_page * SPM_PAGESIZE));
89                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
90
91                 /*
92                 * Reenable RWW-section again. We need this if we want to jump back
93                 * to the application after bootloading.
94                 */
95                 ATOMIC(boot_rww_enable());
96
97                 page_modified = false;
98                 kprintf("Done.\n");
99         }
100 }
101
102
103 /**
104  * Check current page and if \a page is different, load it in
105  * temporary buffer.
106  */
107 static void flash_avr_loadPage(avr_page_t page)
108 {
109         if (page != curr_page)
110         {
111                 flash_avr_flush();
112                 // Load page
113                 memcpy_P(page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
114                 curr_page = page;
115                 kprintf("Loaded page %d\n", curr_page);
116         }
117 }
118
119 /**
120  * Write program memory.
121  * Write \a size bytes from buffer \a _buf to file \a fd
122  * \note Write operations are buffered.
123  */
124 static size_t flash_avr_write(struct _KFile *fd, const void *_buf, size_t size)
125 {
126         const uint8_t *buf =(const uint8_t *)_buf;
127
128         avr_page_t page;
129         avr_page_addr_t page_addr;
130         size_t total_write = 0;
131
132         ASSERT(fd->seek_pos + size <= fd->size);
133         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
134
135         kprintf("Writing at pos[%u]\n", fd->seek_pos);
136         while (size)
137         {
138                 page = fd->seek_pos / SPM_PAGESIZE;
139                 page_addr = fd->seek_pos % SPM_PAGESIZE;
140
141                 flash_avr_loadPage(page);
142
143                 size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
144                 memcpy(page_buf + page_addr, buf, wr_len);
145                 page_modified = true;
146
147                 buf += wr_len;
148                 fd->seek_pos += wr_len;
149                 size -= wr_len;
150                 total_write += wr_len;
151         }
152         kprintf("written %u bytes\n", total_write);
153         return total_write;
154 }
155
156 /**
157  * Open flash file \a fd
158  * \a name and \a mode are unused, cause flash memory is
159  * threated like one file.
160  */
161 static bool flash_avr_open(struct _KFile *fd, UNUSED_ARG(const char *, name), UNUSED_ARG(int, mode))
162 {
163         curr_page = 0;
164         memcpy_P(page_buf, (const char *)(curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
165
166         fd->seek_pos = 0;
167         fd->size = (uint16_t)(FLASHEND - CONFIG_BOOT_SIZE + 1);
168         page_modified = false;
169
170         kprintf("Flash file opened\n");
171         return true;
172 }
173
174 /**
175  * Close file \a fd
176  */
177 static bool flash_avr_close(UNUSED_ARG(struct _KFile *,fd))
178 {
179         flash_avr_flush();
180         kprintf("Flash file closed\n");
181         return true;
182 }
183
184 /**
185  * Move \a fd file seek position of \a offset bytes
186  * from current position.
187  */
188 static int32_t flash_avr_seek(struct _KFile *fd, int32_t offset, int whence)
189 {
190         uint32_t seek_pos;
191
192         switch(whence)
193         {
194                 case SEEK_SET:
195                         seek_pos = 0;
196                         break;
197                 case SEEK_END:
198                         seek_pos = fd->size - 1;
199                         break;
200                 case SEEK_CUR:
201                         seek_pos = fd->seek_pos;
202                         break;
203                 default:
204                         ASSERT(0);
205                         return -1;
206                         break;
207         }
208         
209         /* Bound check */
210         if (seek_pos + offset > fd->size)
211         {
212                 ASSERT(0);
213                 return -1;
214         }
215
216         fd->seek_pos = seek_pos + offset;
217         kprintf("Flash seek to [%u]\n", fd->seek_pos);
218
219         return fd->seek_pos;
220 }
221
222 /**
223  * Read from file \a fd \a size bytes and put it in buffer \a buf
224  * \return the number of bytes read.
225  */
226 static size_t flash_avr_read(struct _KFile *fd, void *buf, size_t size)
227 {
228         ASSERT(fd->seek_pos + size <= fd->size);
229         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
230
231         kprintf("Reading at pos[%u]\n", fd->seek_pos);
232         // Flush current buffered page (if modified).
233         flash_avr_flush();
234
235         /*
236          * AVR pointers are 16 bits wide, this hack is needed to avoid
237          * compiler warning, cause fd->seek_pos is a 32bit offset.
238          */
239         const uint8_t *pgm_addr = (const uint8_t *)0;
240         pgm_addr += fd->seek_pos;
241
242         memcpy_P(buf, pgm_addr, size);
243         fd->seek_pos += size;
244         kprintf("Read %u bytes\n", size);
245         return size;
246 }
247
248 /**
249  * Init AVR flash read/write file.
250  */
251 void flash_avr_init(struct _KFile *fd)
252 {
253         // Set up flash programming functions.
254         fd->open = flash_avr_open;
255         fd->close = flash_avr_close;
256         fd->read = flash_avr_read;
257         fd->write = flash_avr_write;
258         fd->seek = flash_avr_seek;
259 }
260
261 #if CONFIG_TEST
262
263 #define TEST_SIZE 683
264 #define ONE_BYTE_TEST_ADDRESS 347
265
266 uint8_t test_buf[TEST_SIZE];
267 uint8_t save_buf[TEST_SIZE];
268
269 /**
270  * Program memory read/write subtest.
271  * Try to write/read in the same \a f file location \a _size bytes.
272  * \return true if all is ok, false otherwise
273  * \note Restore file position at exit (if no error)
274  * \note Test buffer \a buf must be filled with
275  * the following statement:
276  * <pre>
277  * buf[i] = i & 0xff
278  * </pre>
279  */
280 static bool flash_avr_rwTest(KFile *f, uint8_t *buf, size_t _size)
281 {
282         int32_t size = _size;
283         // Write test buffer
284         if (f->write(f, buf, size) != size)
285                 return false;
286         f->seek(f, -size, SEEK_CUR);
287
288         // Reset test buffer
289         memset(buf, 0, size);
290
291         // Read flash in test buffer
292         if (f->read(f, buf, size) != size)
293                 return false;
294         f->seek(f, -size, SEEK_CUR);
295
296         // Check test result
297         for (size_t i = 0; i < size; i++)
298                 if (buf[i] != (i & 0xff))
299                         return false;
300
301         return true;
302 }
303
304 /**
305  * Test for program memory read/write.
306  */
307 bool flash_avr_test(void)
308 {
309         KFile fd;
310
311         // Set up flash programming functions.
312         flash_avr_init(&fd);
313
314         // Fill in test buffer
315         for (int i = 0; i < TEST_SIZE; i++)
316                 test_buf[i] = (i & 0xff);
317
318         // Open flash
319         fd.open(&fd, NULL, 0);
320         // Save flash content (for later restore).
321         fd.read(&fd, save_buf, sizeof(save_buf));
322         // Seek to addr 0
323         if (fd.seek(&fd, 0, SEEK_SET) != 0)
324                 goto flash_avr_test_end;
325
326         // Test flash read/write to address 0..TEST_SIZE
327         if (!flash_avr_rwTest(&fd, test_buf, TEST_SIZE))
328                 goto flash_avr_test_end;
329
330         // One byte read/write test
331         fd.seek(&fd, ONE_BYTE_TEST_ADDRESS, SEEK_CUR); // Random address
332         if (!flash_avr_rwTest(&fd, test_buf, 1))
333                 goto flash_avr_test_end;
334         fd.seek(&fd, -(int32_t)ONE_BYTE_TEST_ADDRESS, SEEK_CUR);
335
336         // Restore old flash data
337         if (fd.write(&fd, save_buf, sizeof(test_buf)) != TEST_SIZE)
338                 goto flash_avr_test_end;
339         fd.seek(&fd, -TEST_SIZE, SEEK_CUR);
340
341         // Go to the Flash end
342         fd.seek(&fd, -TEST_SIZE, SEEK_END);
343         // Save flash content (for later restore).
344         fd.read(&fd, save_buf, sizeof(save_buf));
345         fd.seek(&fd, -TEST_SIZE, SEEK_CUR);
346
347         // Test flash read/write to address (FLASHEND - TEST_SIZE) ... FLASHEND
348         if (!flash_avr_rwTest(&fd, test_buf, TEST_SIZE))
349                 goto flash_avr_test_end;
350
351         // Go to half test size.
352         fd.seek(&fd, (TEST_SIZE / 2), SEEK_CUR);
353
354         // This test should FAIL, cause we try to write over file end.
355         kprintf("This test should fail.\n");
356         if (flash_avr_rwTest(&fd, test_buf, TEST_SIZE))
357                 goto flash_avr_test_end;
358
359         fd.seek(&fd, -TEST_SIZE, SEEK_CUR);
360         // Restore old flash data
361         fd.write(&fd, save_buf, TEST_SIZE);
362
363         fd.close(&fd);
364         return true;
365
366 flash_avr_test_end:
367         fd.close(&fd);
368         return false;
369 }
370
371 #endif