update to new vusb version
[pub/USBaspLoader.git] / firmware / main.c
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 #else
112 # error "Device signature is not known, please edit main.c!"
113 #endif
114 };
115
116 /* ------------------------------------------------------------------------ */
117
118 static void (*nullVector)(void) __attribute__((__noreturn__));
119
120 static void leaveBootloader()
121 {
122 DBG1(0x01, 0, 0);
123 bootLoaderExit();
124 cli();
125 USB_INTR_ENABLE = 0;
126 USB_INTR_CFG = 0; /* also reset config bits */
127 GICR = (1 << IVCE); /* enable change of interrupt vectors */
128 GICR = (0 << IVSEL); /* move interrupts to application flash section */
129 /* We must go through a global function pointer variable instead of writing
130 * ((void (*)(void))0)();
131 * because the compiler optimizes a constant 0 to "rcall 0" which is not
132 * handled correctly by the assembler.
133 */
134 nullVector();
135 }
136
137 /* ------------------------------------------------------------------------ */
138
139 uchar usbFunctionSetup(uchar data[8])
140 {
141 usbRequest_t *rq = (void *)data;
142 uchar len = 0;
143 static uchar replyBuffer[4];
144
145 usbMsgPtr = replyBuffer;
146 if(rq->bRequest == USBASP_FUNC_TRANSMIT){ /* emulate parts of ISP protocol */
147 uchar rval = 0;
148 usbWord_t address;
149 address.bytes[1] = rq->wValue.bytes[1];
150 address.bytes[0] = rq->wIndex.bytes[0];
151 if(rq->wValue.bytes[0] == 0x30){ /* read signature */
152 rval = rq->wIndex.bytes[0] & 3;
153 rval = signatureBytes[rval];
154 #if HAVE_EEPROM_BYTE_ACCESS
155 }else if(rq->wValue.bytes[0] == 0xa0){ /* read EEPROM byte */
156 rval = eeprom_read_byte((void *)address.word);
157 }else if(rq->wValue.bytes[0] == 0xc0){ /* write EEPROM byte */
158 eeprom_write_byte((void *)address.word, rq->wIndex.bytes[1]);
159 #endif
160 #if HAVE_CHIP_ERASE
161 }else if(rq->wValue.bytes[0] == 0xac && rq->wValue.bytes[1] == 0x80){ /* chip erase */
162 addr_t addr;
163 for(addr = 0; addr < FLASHEND + 1 - 2048; addr += SPM_PAGESIZE) {
164 /* wait and erase page */
165 DBG1(0x33, 0, 0);
166 # ifndef NO_FLASH_WRITE
167 boot_spm_busy_wait();
168 cli();
169 boot_page_erase(addr);
170 sei();
171 # endif
172 }
173 #endif
174 }else{
175 /* ignore all others, return default value == 0 */
176 }
177 replyBuffer[3] = rval;
178 len = 4;
179 }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){
180 /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */
181 len = 1;
182 }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){
183 currentAddress.w[0] = rq->wValue.word;
184 if(rq->bRequest == USBASP_FUNC_SETLONGADDRESS){
185 #if (FLASHEND) > 0xffff
186 currentAddress.w[1] = rq->wIndex.word;
187 #endif
188 }else{
189 bytesRemaining = rq->wLength.bytes[0];
190 /* if(rq->bRequest == USBASP_FUNC_WRITEFLASH) only evaluated during writeFlash anyway */
191 isLastPage = rq->wIndex.bytes[1] & 0x02;
192 #if HAVE_EEPROM_PAGED_ACCESS
193 currentRequest = rq->bRequest;
194 #endif
195 len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */
196 }
197 #if BOOTLOADER_CAN_EXIT
198 }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){
199 requestBootLoaderExit = 1; /* allow proper shutdown/close of connection */
200 #endif
201 }else{
202 /* ignore: USBASP_FUNC_CONNECT */
203 }
204 return len;
205 }
206
207 uchar usbFunctionWrite(uchar *data, uchar len)
208 {
209 uchar isLast;
210
211 DBG1(0x31, (void *)&currentAddress.l, 4);
212 if(len > bytesRemaining)
213 len = bytesRemaining;
214 bytesRemaining -= len;
215 isLast = bytesRemaining == 0;
216 if(currentRequest >= USBASP_FUNC_READEEPROM){
217 uchar i;
218 for(i = 0; i < len; i++){
219 eeprom_write_byte((void *)(currentAddress.w[0]++), *data++);
220 }
221 }else{
222 uchar i;
223 for(i = 0; i < len;){
224 #if !HAVE_CHIP_ERASE
225 if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){ /* if page start: erase */
226 DBG1(0x33, 0, 0);
227 # ifndef NO_FLASH_WRITE
228 cli();
229 boot_page_erase(CURRENT_ADDRESS); /* erase page */
230 sei();
231 boot_spm_busy_wait(); /* wait until page is erased */
232 # endif
233 }
234 #endif
235 i += 2;
236 DBG1(0x32, 0, 0);
237 cli();
238 boot_page_fill(CURRENT_ADDRESS, *(short *)data);
239 sei();
240 CURRENT_ADDRESS += 2;
241 data += 2;
242 /* write page when we cross page boundary or we have the last partial page */
243 if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){
244 DBG1(0x34, 0, 0);
245 #ifndef NO_FLASH_WRITE
246 cli();
247 boot_page_write(CURRENT_ADDRESS - 2);
248 sei();
249 boot_spm_busy_wait();
250 cli();
251 boot_rww_enable();
252 sei();
253 #endif
254 }
255 }
256 DBG1(0x35, (void *)&currentAddress.l, 4);
257 }
258 return isLast;
259 }
260
261 uchar usbFunctionRead(uchar *data, uchar len)
262 {
263 uchar i;
264
265 if(len > bytesRemaining)
266 len = bytesRemaining;
267 bytesRemaining -= len;
268 for(i = 0; i < len; i++){
269 if(currentRequest >= USBASP_FUNC_READEEPROM){
270 *data = eeprom_read_byte((void *)currentAddress.w[0]);
271 }else{
272 *data = pgm_read_byte((void *)CURRENT_ADDRESS);
273 }
274 data++;
275 CURRENT_ADDRESS++;
276 }
277 return len;
278 }
279
280 /* ------------------------------------------------------------------------ */
281
282 static void initForUsbConnectivity(void)
283 {
284 uchar i = 0;
285
286 usbInit();
287 /* enforce USB re-enumerate: */
288 usbDeviceDisconnect(); /* do this while interrupts are disabled */
289 while(--i){ /* fake USB disconnect for > 250 ms */
290 wdt_reset();
291 _delay_ms(1);
292 }
293 usbDeviceConnect();
294 sei();
295 }
296
297 int __attribute__((noreturn)) main(void)
298 {
299 /* initialize */
300 wdt_disable(); /* main app may have enabled watchdog */
301 bootLoaderInit();
302 odDebugInit();
303 DBG1(0x00, 0, 0);
304 #ifndef NO_FLASH_WRITE
305 GICR = (1 << IVCE); /* enable change of interrupt vectors */
306 GICR = (1 << IVSEL); /* move interrupts to boot flash section */
307 #endif
308 if(bootLoaderCondition()){
309 uchar i = 0, j = 0;
310 initForUsbConnectivity();
311 do{
312 usbPoll();
313 #if BOOTLOADER_CAN_EXIT
314 if(requestBootLoaderExit){
315 if(--i == 0){
316 if(--j == 0)
317 break;
318 }
319 }
320 #endif
321 }while(bootLoaderCondition()); /* main event loop */
322 }
323 leaveBootloader();
324 }
325
326 /* ------------------------------------------------------------------------ */