Merge from triface.
[bertos.git] / mware / prog_avr.c
1 /**
2  * \file
3  * <!--
4  * Copyright 2007 Develer S.r.l. (http://www.develer.com/)
5  * All Rights Reserved.
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
15 #include "prog.h"
16
17 #include <string.h>
18
19 #include <drv/wdt.h>
20 #include <cfg/macros.h> // MIN()
21 #include <cfg/compiler.h>
22 #include <cfg/debug.h>
23
24 #include <avr/io.h>
25 #include <avr/boot.h>
26 #include <avr/pgmspace.h>
27
28 typedef uint16_t avr_page_addr_t;
29 typedef uint16_t avr_page_t;
30
31 /**
32  * Temporary buffer cointaing data block to
33  * write on flash.
34  */
35 static uint8_t page_buf[SPM_PAGESIZE];
36
37 bool page_modified; /// Flag for checking if current page is modified.
38
39 /**
40  * Current buffered page.
41  */
42 static avr_page_t curr_page = 0;
43
44 /**
45  * Write current buffered page in flash memory (if modified).
46  * This function erase flash memory page before writing.
47  */
48 static void prog_flush(void)
49 {
50         if (page_modified)
51         {
52                 kprintf("Flushing page %d\n", curr_page);
53
54                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
55
56                 kprintf("Filling temparary page buffer...");
57                 /* Fill the temporary buffer of the AVR */
58                 for (avr_page_addr_t page_addr = 0; page_addr < SPM_PAGESIZE; page_addr += 2)
59                 {
60                         uint16_t word = ((uint16_t)page_buf[page_addr + 1] << 8) | page_buf[page_addr];
61
62                         ATOMIC(boot_page_fill(page_addr, word));
63                 }
64                 kprintf("Done.\n");
65
66                 wdt_reset();
67
68                 kprintf("Erasing page, addr %d...", curr_page * SPM_PAGESIZE);
69
70                 /* Page erase */
71                 ATOMIC(boot_page_erase(curr_page * SPM_PAGESIZE));
72
73                 /* Wait until the memory is erased. */
74                 boot_spm_busy_wait();
75
76                 kprintf("Done.\n");
77                 kprintf("Writing page, addr %d...", curr_page * SPM_PAGESIZE);
78
79                 /* Store buffer in flash page. */
80                 ATOMIC(boot_page_write(curr_page * SPM_PAGESIZE));
81                 boot_spm_busy_wait();  // Wait while the SPM instruction is busy.
82
83                 /*
84                 * Reenable RWW-section again. We need this if we want to jump back
85                 * to the application after bootloading.
86                 */
87                 ATOMIC(boot_rww_enable());
88
89                 page_modified = false;
90                 kprintf("Done.\n");
91         }
92 }
93
94
95 /**
96  * Check current page and if \param page is different, load it in
97  * temporary buffer.
98  */
99 static void prog_loadPage(avr_page_t page)
100 {
101         if (page != curr_page)
102         {
103                 prog_flush();
104                 // Load page
105                 memcpy_P(page_buf, (const char *)(page * SPM_PAGESIZE), SPM_PAGESIZE);
106                 curr_page = page;
107                 kprintf("Loaded page %d\n", curr_page);
108         }
109 }
110
111 /**
112  * Write program memory.
113  * Write \param size bytes from buffer \param buf to file \param *fd
114  * \note Write operations are buffered.
115  */
116 size_t prog_write(struct _KFile *fd, const void *_buf, size_t size)
117 {
118         const uint8_t *buf =(const uint8_t *)_buf;
119
120         avr_page_t page;
121         avr_page_addr_t page_addr;
122         size_t total_write = 0;
123
124         ASSERT(fd->seek_pos + size <= fd->size);
125         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
126
127         kprintf("Writing at pos[%d]\n", fd->seek_pos);
128         while (size)
129         {
130                 page = fd->seek_pos / SPM_PAGESIZE;
131                 page_addr = fd->seek_pos % SPM_PAGESIZE;
132
133                 prog_loadPage(page);
134
135                 size_t wr_len = MIN(size, SPM_PAGESIZE - page_addr);
136                 memcpy(page_buf + page_addr, buf, wr_len);
137                 page_modified = true;
138
139                 buf += wr_len;
140                 fd->seek_pos += wr_len;
141                 size -= wr_len;
142                 total_write += wr_len;
143         }
144         kprintf("written %d bytes\n", total_write);
145         return total_write;
146 }
147
148 /**
149  * Open flash file \param *fd.
150  * \param name and \param mode are unused, cause flash memory is
151  * threated like one file.
152  */
153 bool prog_open(struct _KFile *fd, UNUSED_ARG(const char *, name), UNUSED_ARG(int, mode))
154 {
155         curr_page = 0;
156         memcpy_P(page_buf, (const char *)(curr_page * SPM_PAGESIZE), SPM_PAGESIZE);
157
158         fd->seek_pos = 0;
159         fd->size = (uint16_t)(FLASHEND - CONFIG_BOOT_SIZE + 1);
160         page_modified = false;
161
162         kprintf("Flash file opened\n");
163         return true;
164 }
165
166 /**
167  * Close file \param *fd
168  */
169 bool prog_close(UNUSED_ARG(struct _KFile *,fd))
170 {
171         prog_flush();
172         kprintf("Flash file closed\n");
173         return true;
174 }
175
176 /**
177  * Move \param *fd file seek position of \param offset bytes
178  * from current position.
179  */
180 bool prog_seek(struct _KFile *fd, int32_t offset)
181 {
182         ASSERT(fd->seek_pos + offset <= fd->size);
183
184         /* Bound check */
185         if (fd->seek_pos + offset > fd->size)
186                 return false;
187
188         fd->seek_pos += offset;
189         kprintf("Flash seek to [%d]\n", fd->seek_pos);
190
191         return true;
192 }
193
194 /**
195  * Read from file \param *fd \param size bytes and put it in buffer \param *buf
196  * \return the number of bytes read.
197  */
198 size_t  prog_read(struct _KFile *fd, void *buf, size_t size)
199 {
200         ASSERT(fd->seek_pos + size <= fd->size);
201         size = MIN((uint32_t)size, fd->size - fd->seek_pos);
202
203         kprintf("Reading at pos[%d]\n", fd->seek_pos);
204         // Flush current buffered page (if modified).
205         prog_flush();
206
207         /*
208          * AVR pointers are 16 bits wide, this hack is needed to avoid
209          * compiler warning, cause fd->seek_pos is a 32bit offset.
210          */
211         const uint8_t *pgm_addr = (const uint8_t *)0;
212         pgm_addr += fd->seek_pos;
213
214         memcpy_P(buf, pgm_addr, size);
215         fd->seek_pos += size;
216         kprintf("Read %d bytes\n", size);
217         return size;
218 }
219