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