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