new feature "HAVE_UNPRECISEWAIT" for save some flash
[pub/USBaspLoader.git] / updater / updater.c
1 #ifndef F_CPU
2 #define F_CPU 1000000UL /* 1 Mhz-Takt; hier richtigen Wert eintragen */
3 #endif
4
5 // 1) Test the "changed" - feature by debugging a LED-PIN to high (PB0?)
6 // 2) Test the "needs_erase" - feature by debugging a LED-PIN to high (PB1?)
7 // #define mypgmdebug 1
8
9 #include "../firmware/spminterface.h"
10 #include "usbasploader.h"
11
12 #include <avr/io.h>
13 #include <avr/interrupt.h>
14 #include <avr/wdt.h>
15
16 #include <avr/pgmspace.h>
17
18 #include <stdint.h>
19
20 #include <util/delay.h>
21 #include <string.h>
22
23 #include "../firmware/bootloaderconfig.h"
24 #if !HAVE_SPMINTEREFACE
25 #error "bootloader does not support updating itself! (HAVE_SPMINTEREFACE)"
26 #endif
27
28 // helpful definitions and makros ////
29
30 #ifndef NEW_BOOTLOADER_ADDRESS
31 #error "where should the new bootloader be positioned?"
32 #endif
33
34
35
36 #if (NEW_BOOTLOADER_ADDRESS != (funcaddr___bootloader__do_spm-(funcaddr___bootloader__do_spm % SPM_PAGESIZE)))
37 #warning the new bootloader seems to be located elsewhere, than the current one!
38 #endif
39
40 #ifndef NEW_SPM_ADDRESS
41 #warning I do not know where new "bootloader__do_spm" is located - assuming "NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE)"
42 #define NEW_SPM_ADDRESS (NEW_BOOTLOADER_ADDRESS+(funcaddr___bootloader__do_spm % SPM_PAGESIZE))
43 #endif
44
45 // TEMP_SPM supports up to 4 pages for "bootloader__do_spm"...
46 #define TEMP_SPM_NUMPAGE 4
47 #define TEMP_SPM_BLKSIZE (TEMP_SPM_NUMPAGE*SPM_PAGESIZE)
48 #ifndef TEMP_SPM_PAGEADR
49 #warning "TEMP_SPM_PAGEADR" is not defined explicitly - will choose END OF FLASH !
50 #define TEMP_SPM_PAGEADR ((FLASHEND+1) - TEMP_SPM_BLKSIZE)
51 #endif
52 #define TEMP_SPM_ADDRESS ((TEMP_SPM_PAGEADR) + (funcaddr___bootloader__do_spm % SPM_PAGESIZE))
53
54
55 #if (NEW_SPM_ADDRESS == funcaddr___bootloader__do_spm)
56 #define new_do_spm do_spm
57 #else
58 void new_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
59 __do_spm_Ex(flash_byteaddress, spmcrval, dataword, NEW_SPM_ADDRESS >> 1);
60 }
61 #endif
62
63 void temp_do_spm(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword) {
64 __do_spm_Ex(flash_byteaddress, spmcrval, dataword, TEMP_SPM_ADDRESS >> 1);
65 }
66
67
68
69 // some important consistency checks ////
70
71 //check if "NEW_BOOTLOADER_ADDRESS" is page-aligned
72 #if (NEW_BOOTLOADER_ADDRESS % SPM_PAGESIZE != 0)
73 #error "NEW_BOOTLOADER_ADDRESS" is not aligned to pages!
74 #endif
75
76 //check, if NEW_SPM_ADDRESS is an even address
77 #if ((NEW_SPM_ADDRESS % 2) != 0)
78 #error NEW_SPM_ADDRESS must be an even address, since it contains executable code!
79 #endif
80
81
82
83 //check, if TEMP_SPM somehow overlaps with old SPM
84 #if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= funcaddr___bootloader__do_spm) && (TEMP_SPM_ADDRESS <= (funcaddr___bootloader__do_spm + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
85 #error TEMP_SPM_ADDRESS overlaps "funcaddr___bootloader__do_spm"!
86 #endif
87
88 //check, if TEMP_SPM somehow overlaps with new SPM
89 #if (((TEMP_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE) >= NEW_SPM_ADDRESS) && (TEMP_SPM_ADDRESS <= (NEW_SPM_ADDRESS + TEMP_SPM_BLKSIZE + SPM_PAGESIZE)))
90 #error TEMP_SPM_ADDRESS overlaps "NEW_SPM_ADDRESS"!
91 #endif
92
93 //check, if TEMP_SPM_ADDRESS is an even address
94 #if ((TEMP_SPM_ADDRESS % 2) != 0)
95 #error TEMP_SPM_ADDRESS must be an even address, since it contains executable code!
96 #endif
97
98 //check, if TEMP_SPM_ADDRESS fits into flash
99 #if ((TEMP_SPM_PAGEADR + TEMP_SPM_BLKSIZE) > (FLASHEND+1))
100 #error TEMP_SPM_ADDRESS exceeds flashend!
101 #endif
102
103 //check if size too low
104 #if (SIZEOF_new_firmware <= (TEMP_SPM_BLKSIZE + (NEW_SPM_ADDRESS - NEW_BOOTLOADER_ADDRESS)))
105 #error empty firmware!
106 #endif
107
108 //check if size too high
109 #if (SIZEOF_new_firmware > ((FLASHEND+1)-NEW_BOOTLOADER_ADDRESS))
110 #error firmware too big! firmware does not fit into flash memory!
111 #endif
112
113 // main code ////
114
115 /*
116 * in this case a near address
117 */
118 typedef uint32_t mypgm_addr_t;
119 typedef void (*mypgm_spminterface)(const uint32_t flash_byteaddress, const uint8_t spmcrval, const uint16_t dataword);
120
121
122 #if 0
123 size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
124 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
125 size_t pagesize = result >> 1;
126 uint16_t *pagedata = (void*)buffer;
127 mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
128 size_t i;
129
130 for (i=0;i<pagesize;i+=1) {
131 pagedata[i]=pgm_read_word(pageaddr);
132 pageaddr+=2;
133 }
134
135 return result;
136 }
137 #else
138 // replace it somehow with "memcpy_PF" in order to save some ops...
139 size_t mypgm_readpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize) {
140 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
141 mypgm_addr_t pageaddr = byteaddress - (byteaddress % SPM_PAGESIZE);
142
143 memcpy_PF((void*)buffer, (uint_farptr_t)pageaddr, result);
144
145 return result;
146 }
147 #endif
148
149 size_t mypgm_WRITEpage(const mypgm_addr_t byteaddress,const void* buffer, const size_t bufferbytesize, mypgm_spminterface spmfunc) {
150 size_t result = (bufferbytesize < SPM_PAGESIZE)?bufferbytesize:SPM_PAGESIZE;
151 size_t pagesize = result >> 1;
152 uint16_t *pagedata = (void*)buffer;
153 mypgm_addr_t pageaddr_bakup = byteaddress - (byteaddress % SPM_PAGESIZE);
154 mypgm_addr_t pageaddr = pageaddr_bakup;
155
156 uint8_t changed=0, needs_erase=0;
157 uint16_t deeword;
158 size_t i;
159
160 // just check, if page needs a rewrite or an erase...
161 for (i=0;i<pagesize;i+=1) {
162 deeword=pgm_read_word(pageaddr);
163
164 if (deeword != pagedata[i]) changed=1;
165
166 /*
167 * deeword = x
168 * buffer = y
169 *
170 * 1 ? 1 ==> 1
171 * 1 ? 0 ==> 1
172 * 0 ? 1 ==> 0
173 * 0 ? 0 ==> 1
174 *
175 * ==> /(/x * y) ==> x + /y
176 */
177 deeword |= ~pagedata[i];
178 if ((~deeword) != 0) needs_erase=1;
179
180 pageaddr+=2;
181 }
182
183 if (changed) {
184 #ifdef mypgmdebug
185 DDRB |= (1<<PB0);
186 PORTB |= (1<<PB0);
187 #endif
188
189 if (needs_erase) {
190 //do a page-erase, ATTANTION: flash only can be erased a limited number of times !
191 spmfunc(pageaddr_bakup, 0x3, 0);
192 #ifndef mypgmdebug
193 }
194 #else
195 } else {
196 DDRB |= (1<<PB1);
197 PORTB |= (1<<PB1);
198 }
199 #endif
200
201
202 // from now on, the page is assumed empty !! (hopefully our code is located somewhere else!)
203 // now, fill the tempoary buffer
204 // ATTANTION: see comment on "do_spm" !
205 pageaddr = pageaddr_bakup;
206 for (i=0;i<pagesize;i+=1) {
207 spmfunc(pageaddr, 0x1, pagedata[i]);
208 pageaddr+=2;
209 }
210
211 // so, now finally write the page to the FLASH
212 spmfunc(pageaddr_bakup, 0x5, 0);
213 } else {
214 // no change - no write...
215 result = 0;
216 }
217
218
219 return result;
220 }
221
222 // #pragma GCC diagnostic ignored "-Wno-pointer-to-int-cast"
223 int main(void)
224 {
225 size_t i;
226 uint8_t buffer[SPM_PAGESIZE];
227
228 wdt_disable();
229 cli();
230
231 #ifdef mypgmdebug
232 DDRD |= (1<<PD3);
233 DDRD |= (1<<PD5);
234 PORTD |= (1<<PD3);
235 PORTD |= (1<<PD5);
236 #endif
237
238
239
240 // check if firmware would change...
241 buffer[0]=0;
242 for (i=0;i<SIZEOF_new_firmware;i+=2) {
243 uint16_t a, b;
244 a=pgm_read_word((void*)&new_firmware[i]);
245 b=pgm_read_word(NEW_BOOTLOADER_ADDRESS+i);
246 if (a!=b) {
247 buffer[0]=1;
248 break;
249 }
250 }
251
252
253
254 // need to change the firmware...
255 if (buffer[0]) {
256
257
258 // A
259 // copy the current "bootloader__do_spm" to tempoary position via std. "bootloader__do_spm"
260 for (i=0;i<TEMP_SPM_BLKSIZE;i+=SPM_PAGESIZE) {
261 mypgm_WRITEpage(TEMP_SPM_PAGEADR+i, buffer, mypgm_readpage(funcaddr___bootloader__do_spm+i, buffer, sizeof(buffer)), do_spm);
262 #ifdef mypgmdebug
263 PORTD ^= (1<<PD3);
264 _delay_ms(500);
265 PORTD ^= (1<<PD3);
266 _delay_ms(500);
267 #endif
268 }
269
270 // B
271 // start updating the firmware to "NEW_BOOTLOADER_ADDRESS" until at least "TEMP_SPM_BLKSIZE"-bytes after "NEW_SPM_ADDRESS" were written
272 // therefore use the tempoary "bootloader__do_spm" (since we most probably will overwrite the default do_spm)
273 for (i=0;;i+=SPM_PAGESIZE) {
274 memset((void*)buffer, 0xff, sizeof(buffer));
275 memcpy_PF((void*)buffer, (uint_farptr_t)((void*)&new_firmware[i]), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
276
277 mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), temp_do_spm);
278
279 #ifdef mypgmdebug
280 PORTD ^= (1<<PD3);
281 _delay_ms(500);
282 PORTD ^= (1<<PD3);
283 _delay_ms(500);
284 #endif
285
286 if ((NEW_BOOTLOADER_ADDRESS+i) > (NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE)) break;
287 }
288
289 // C
290 // continue writeing the new_firmware after "NEW_SPM_ADDRESS+TEMP_SPM_BLKSIZE" this time use the "new_do_spm"
291 for (;i<SIZEOF_new_firmware;i+=SPM_PAGESIZE) {
292 memset((void*)buffer, 0xff, sizeof(buffer));
293 memcpy_PF((void*)buffer, (uint_farptr_t)((void*)&new_firmware[i]), ((SIZEOF_new_firmware-i)>sizeof(buffer))?sizeof(buffer):(SIZEOF_new_firmware-i));
294
295 mypgm_WRITEpage(NEW_BOOTLOADER_ADDRESS+i, buffer, sizeof(buffer), new_do_spm);
296
297 #ifdef mypgmdebug
298 PORTD ^= (1<<PD3);
299 _delay_ms(500);
300 PORTD ^= (1<<PD3);
301 _delay_ms(500);
302 #endif
303 }
304
305
306
307 }
308
309 #ifdef mypgmdebug
310 PORTD |= (1<<PD3);
311 #endif
312 while(1)
313 {
314 #ifdef mypgmdebug
315 PORTD ^= (1<<PD5);
316 _delay_ms(100);
317 #endif
318 }
319
320 }