1 | /* Name: main.c
|
2 | * Project: USBaspLoader
|
3 | * Author: Christian Starkjohann
|
4 | * Creation Date: 2007-12-08
|
5 | * Tabsize: 4
|
6 | * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
|
7 | * License: GNU GPL v2 (see License.txt)
|
8 | * This Revision: $Id: main.c 786 2010-05-30 20:41:40Z cs $
|
9 | */
|
10 |
|
11 | #include <avr/io.h>
|
12 | #include <avr/interrupt.h>
|
13 | #include <avr/pgmspace.h>
|
14 | #include <avr/wdt.h>
|
15 | #include <avr/boot.h>
|
16 | #include <avr/eeprom.h>
|
17 | #include <util/delay.h>
|
18 | #include <string.h>
|
19 |
|
20 | static void leaveBootloader() __attribute__((__noreturn__));
|
21 |
|
22 | #include "bootloaderconfig.h"
|
23 | #include "usbdrv/usbdrv.c"
|
24 |
|
25 | /* ------------------------------------------------------------------------ */
|
26 |
|
27 | /* Request constants used by USBasp */
|
28 | #define USBASP_FUNC_CONNECT 1
|
29 | #define USBASP_FUNC_DISCONNECT 2
|
30 | #define USBASP_FUNC_TRANSMIT 3
|
31 | #define USBASP_FUNC_READFLASH 4
|
32 | #define USBASP_FUNC_ENABLEPROG 5
|
33 | #define USBASP_FUNC_WRITEFLASH 6
|
34 | #define USBASP_FUNC_READEEPROM 7
|
35 | #define USBASP_FUNC_WRITEEEPROM 8
|
36 | #define USBASP_FUNC_SETLONGADDRESS 9
|
37 |
|
38 | /* ------------------------------------------------------------------------ */
|
39 |
|
40 | #ifndef ulong
|
41 | # define ulong unsigned long
|
42 | #endif
|
43 | #ifndef uint
|
44 | # define uint unsigned int
|
45 | #endif
|
46 |
|
47 | /* defaults if not in config file: */
|
48 | #ifndef HAVE_EEPROM_PAGED_ACCESS
|
49 | # define HAVE_EEPROM_PAGED_ACCESS 0
|
50 | #endif
|
51 | #ifndef HAVE_EEPROM_BYTE_ACCESS
|
52 | # define HAVE_EEPROM_BYTE_ACCESS 0
|
53 | #endif
|
54 | #ifndef BOOTLOADER_CAN_EXIT
|
55 | # define BOOTLOADER_CAN_EXIT 0
|
56 | #endif
|
57 |
|
58 | /* allow compatibility with avrusbboot's bootloaderconfig.h: */
|
59 | #ifdef BOOTLOADER_INIT
|
60 | # define bootLoaderInit() BOOTLOADER_INIT
|
61 | # define bootLoaderExit()
|
62 | #endif
|
63 | #ifdef BOOTLOADER_CONDITION
|
64 | # define bootLoaderCondition() BOOTLOADER_CONDITION
|
65 | #endif
|
66 |
|
67 | /* device compatibility: */
|
68 | #ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */
|
69 | # define GICR MCUCR
|
70 | #endif
|
71 |
|
72 | /* ------------------------------------------------------------------------ */
|
73 |
|
74 | #if (FLASHEND) > 0xffff /* we need long addressing */
|
75 | # define CURRENT_ADDRESS currentAddress.l
|
76 | # define addr_t ulong
|
77 | #else
|
78 | # define CURRENT_ADDRESS currentAddress.w[0]
|
79 | # define addr_t uint
|
80 | #endif
|
81 |
|
82 | typedef union longConverter{
|
83 | addr_t l;
|
84 | uint w[sizeof(addr_t)/2];
|
85 | uchar b[sizeof(addr_t)];
|
86 | }longConverter_t;
|
87 |
|
88 | static uchar requestBootLoaderExit;
|
89 | static longConverter_t currentAddress; /* in bytes */
|
90 | static uchar bytesRemaining;
|
91 | static uchar isLastPage;
|
92 | #if HAVE_EEPROM_PAGED_ACCESS
|
93 | static uchar currentRequest;
|
94 | #else
|
95 | static const uchar currentRequest = 0;
|
96 | #endif
|
97 |
|
98 | static const uchar signatureBytes[4] = {
|
99 | #ifdef SIGNATURE_BYTES
|
100 | SIGNATURE_BYTES
|
101 | #elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__)
|
102 | 0x1e, 0x93, 0x07, 0
|
103 | #elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__)
|
104 | 0x1e, 0x92, 0x05, 0
|
105 | #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__)
|
106 | 0x1e, 0x93, 0x0a, 0
|
107 | #elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__)
|
108 | 0x1e, 0x94, 0x06, 0
|
109 | #elif defined (__AVR_ATmega328P__)
|
110 | 0x1e, 0x95, 0x0f, 0
|
111 | #elif defined (__AVR_ATmega32__)
|
112 | 0x1e, 0x95, 0x02, 0
|
113 | #else
|
114 | # error "Device signature is not known, please edit main.c!"
|
115 | #endif
|
116 | };
|
117 |
|
118 | /* ------------------------------------------------------------------------ */
|
119 |
|
120 | static void (*nullVector)(void) __attribute__((__noreturn__));
|
121 |
|
122 | static void leaveBootloader()
|
123 | {
|
124 | DBG1(0x01, 0, 0);
|
125 | bootLoaderExit();
|
126 | cli();
|
127 | USB_INTR_ENABLE = 0;
|
128 | USB_INTR_CFG = 0; /* also reset config bits */
|
129 | GICR = (1 << IVCE); /* enable change of interrupt vectors */
|
130 | GICR = (0 << IVSEL); /* move interrupts to application flash section */
|
131 | /* We must go through a global function pointer variable instead of writing
|
132 | * ((void (*)(void))0)();
|
133 | * because the compiler optimizes a constant 0 to "rcall 0" which is not
|
134 | * handled correctly by the assembler.
|
135 | */
|
136 | nullVector();
|
137 | }
|
138 |
|
139 | /* ------------------------------------------------------------------------ */
|
140 |
|
141 | uchar usbFunctionSetup(uchar data[8])
|
142 | {
|
143 | usbRequest_t *rq = (void *)data;
|
144 | uchar len = 0;
|
145 | static uchar replyBuffer[4];
|
146 |
|
147 | usbMsgPtr = replyBuffer;
|
148 | if(rq->bRequest == USBASP_FUNC_TRANSMIT){ /* emulate parts of ISP protocol */
|
149 | uchar rval = 0;
|
150 | usbWord_t address;
|
151 | address.bytes[1] = rq->wValue.bytes[1];
|
152 | address.bytes[0] = rq->wIndex.bytes[0];
|
153 | if(rq->wValue.bytes[0] == 0x30){ /* read signature */
|
154 | rval = rq->wIndex.bytes[0] & 3;
|
155 | rval = signatureBytes[rval];
|
156 | #if HAVE_EEPROM_BYTE_ACCESS
|
157 | }else if(rq->wValue.bytes[0] == 0xa0){ /* read EEPROM byte */
|
158 | rval = eeprom_read_byte((void *)address.word);
|
159 | }else if(rq->wValue.bytes[0] == 0xc0){ /* write EEPROM byte */
|
160 | eeprom_write_byte((void *)address.word, rq->wIndex.bytes[1]);
|
161 | #endif
|
162 | #if HAVE_CHIP_ERASE
|
163 | }else if(rq->wValue.bytes[0] == 0xac && rq->wValue.bytes[1] == 0x80){ /* chip erase */
|
164 | addr_t addr;
|
165 | for(addr = 0; addr < FLASHEND + 1 - 2048; addr += SPM_PAGESIZE) {
|
166 | /* wait and erase page */
|
167 | DBG1(0x33, 0, 0);
|
168 | # ifndef NO_FLASH_WRITE
|
169 | boot_spm_busy_wait();
|
170 | cli();
|
171 | boot_page_erase(addr);
|
172 | sei();
|
173 | # endif
|
174 | }
|
175 | #endif
|
176 | }else{
|
177 | /* ignore all others, return default value == 0 */
|
178 | }
|
179 | replyBuffer[3] = rval;
|
180 | len = 4;
|
181 | }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){
|
182 | /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */
|
183 | len = 1;
|
184 | }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){
|
185 | currentAddress.w[0] = rq->wValue.word;
|
186 | if(rq->bRequest == USBASP_FUNC_SETLONGADDRESS){
|
187 | #if (FLASHEND) > 0xffff
|
188 | currentAddress.w[1] = rq->wIndex.word;
|
189 | #endif
|
190 | }else{
|
191 | bytesRemaining = rq->wLength.bytes[0];
|
192 | /* if(rq->bRequest == USBASP_FUNC_WRITEFLASH) only evaluated during writeFlash anyway */
|
193 | isLastPage = rq->wIndex.bytes[1] & 0x02;
|
194 | #if HAVE_EEPROM_PAGED_ACCESS
|
195 | currentRequest = rq->bRequest;
|
196 | #endif
|
197 | len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */
|
198 | }
|
199 | #if BOOTLOADER_CAN_EXIT
|
200 | }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){
|
201 | requestBootLoaderExit = 1; /* allow proper shutdown/close of connection */
|
202 | #endif
|
203 | }else{
|
204 | /* ignore: USBASP_FUNC_CONNECT */
|
205 | }
|
206 | return len;
|
207 | }
|
208 |
|
209 | uchar usbFunctionWrite(uchar *data, uchar len)
|
210 | {
|
211 | uchar isLast;
|
212 |
|
213 | DBG1(0x31, (void *)¤tAddress.l, 4);
|
214 | if(len > bytesRemaining)
|
215 | len = bytesRemaining;
|
216 | bytesRemaining -= len;
|
217 | isLast = bytesRemaining == 0;
|
218 | if(currentRequest >= USBASP_FUNC_READEEPROM){
|
219 | uchar i;
|
220 | for(i = 0; i < len; i++){
|
221 | eeprom_write_byte((void *)(currentAddress.w[0]++), *data++);
|
222 | }
|
223 | }else{
|
224 | uchar i;
|
225 | for(i = 0; i < len;){
|
226 | #if !HAVE_CHIP_ERASE
|
227 | if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){ /* if page start: erase */
|
228 | DBG1(0x33, 0, 0);
|
229 | # ifndef NO_FLASH_WRITE
|
230 | cli();
|
231 | boot_page_erase(CURRENT_ADDRESS); /* erase page */
|
232 | sei();
|
233 | boot_spm_busy_wait(); /* wait until page is erased */
|
234 | # endif
|
235 | }
|
236 | #endif
|
237 | i += 2;
|
238 | DBG1(0x32, 0, 0);
|
239 | cli();
|
240 | boot_page_fill(CURRENT_ADDRESS, *(short *)data);
|
241 | sei();
|
242 | CURRENT_ADDRESS += 2;
|
243 | data += 2;
|
244 | /* write page when we cross page boundary or we have the last partial page */
|
245 | if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){
|
246 | DBG1(0x34, 0, 0);
|
247 | #ifndef NO_FLASH_WRITE
|
248 | cli();
|
249 | boot_page_write(CURRENT_ADDRESS - 2);
|
250 | sei();
|
251 | boot_spm_busy_wait();
|
252 | cli();
|
253 | boot_rww_enable();
|
254 | sei();
|
255 | #endif
|
256 | }
|
257 | }
|
258 | DBG1(0x35, (void *)¤tAddress.l, 4);
|
259 | }
|
260 | return isLast;
|
261 | }
|
262 |
|
263 | uchar usbFunctionRead(uchar *data, uchar len)
|
264 | {
|
265 | uchar i;
|
266 |
|
267 | if(len > bytesRemaining)
|
268 | len = bytesRemaining;
|
269 | bytesRemaining -= len;
|
270 | for(i = 0; i < len; i++){
|
271 | if(currentRequest >= USBASP_FUNC_READEEPROM){
|
272 | *data = eeprom_read_byte((void *)currentAddress.w[0]);
|
273 | }else{
|
274 | *data = pgm_read_byte((void *)CURRENT_ADDRESS);
|
275 | }
|
276 | data++;
|
277 | CURRENT_ADDRESS++;
|
278 | }
|
279 | return len;
|
280 | }
|
281 |
|
282 | /* ------------------------------------------------------------------------ */
|
283 |
|
284 | static void initForUsbConnectivity(void)
|
285 | {
|
286 | uchar i = 0;
|
287 |
|
288 | usbInit();
|
289 | /* enforce USB re-enumerate: */
|
290 | usbDeviceDisconnect(); /* do this while interrupts are disabled */
|
291 | while(--i){ /* fake USB disconnect for > 250 ms */
|
292 | wdt_reset();
|
293 | _delay_ms(1);
|
294 | }
|
295 | usbDeviceConnect();
|
296 | sei();
|
297 | }
|
298 |
|
299 | int __attribute__((noreturn)) main(void)
|
300 | {
|
301 | /* initialize */
|
302 | wdt_disable(); /* main app may have enabled watchdog */
|
303 | bootLoaderInit();
|
304 | odDebugInit();
|
305 | DBG1(0x00, 0, 0);
|
306 |
|
307 | #ifndef NO_FLASH_WRITE
|
308 | GICR = (1 << IVCE); /* enable change of interrupt vectors */
|
309 | GICR = (1 << IVSEL); /* move interrupts to boot flash section */
|
310 | #endif
|
311 | if(bootLoaderCondition()){
|
312 | uchar i = 0, j = 0;
|
313 | initForUsbConnectivity();
|
314 | do{
|
315 | usbPoll();
|
316 | #if BOOTLOADER_CAN_EXIT
|
317 | if(requestBootLoaderExit){
|
318 | if(--i == 0){
|
319 | if(--j == 0)
|
320 | break;
|
321 | }
|
322 | }
|
323 | #endif
|
324 | }while(bootLoaderCondition()); /* main event loop */
|
325 | }
|
326 | leaveBootloader();
|
327 | }
|
328 |
|
329 | /* ------------------------------------------------------------------------ */
|