2 * Project: USBaspLoader
3 * Author: Christian Starkjohann
4 * Creation Date: 2007-12-08
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 $
12 #include <avr/interrupt.h>
13 #include <avr/pgmspace.h>
16 #include <avr/eeprom.h>
17 #include <util/delay.h>
20 static void leaveBootloader() __attribute__((__noreturn__
));
22 #include "bootloaderconfig.h"
23 #include "usbdrv/usbdrv.c"
25 /* ------------------------------------------------------------------------ */
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
38 /* ------------------------------------------------------------------------ */
41 # define ulong unsigned long
44 # define uint unsigned int
47 /* defaults if not in config file: */
48 #ifndef HAVE_EEPROM_PAGED_ACCESS
49 # define HAVE_EEPROM_PAGED_ACCESS 0
51 #ifndef HAVE_EEPROM_BYTE_ACCESS
52 # define HAVE_EEPROM_BYTE_ACCESS 0
54 #ifndef BOOTLOADER_CAN_EXIT
55 # define BOOTLOADER_CAN_EXIT 0
58 /* allow compatibility with avrusbboot's bootloaderconfig.h: */
59 #ifdef BOOTLOADER_INIT
60 # define bootLoaderInit() BOOTLOADER_INIT
61 # define bootLoaderExit()
63 #ifdef BOOTLOADER_CONDITION
64 # define bootLoaderCondition() BOOTLOADER_CONDITION
67 /* device compatibility: */
68 #ifndef GICR /* ATMega*8 don't have GICR, use MCUCR instead */
72 /* ------------------------------------------------------------------------ */
74 #if (FLASHEND) > 0xffff /* we need long addressing */
75 # define CURRENT_ADDRESS currentAddress.l
78 # define CURRENT_ADDRESS currentAddress.w[0]
82 typedef union longConverter
{
84 uint w
[sizeof(addr_t
)/2];
85 uchar b
[sizeof(addr_t
)];
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
;
95 static const uchar currentRequest
= 0;
98 static const uchar signatureBytes
[4] = {
99 #ifdef SIGNATURE_BYTES
101 #elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__)
103 #elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__)
105 #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__)
107 #elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__)
109 #elif defined (__AVR_ATmega328P__)
112 # error "Device signature is not known, please edit main.c!"
116 /* ------------------------------------------------------------------------ */
118 static void (*nullVector
)(void) __attribute__((__noreturn__
));
120 static void leaveBootloader()
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.
137 /* ------------------------------------------------------------------------ */
139 uchar
usbFunctionSetup(uchar data
[8])
141 usbRequest_t
*rq
= (void *)data
;
143 static uchar replyBuffer
[4];
145 usbMsgPtr
= replyBuffer
;
146 if(rq
->bRequest
== USBASP_FUNC_TRANSMIT
){ /* emulate parts of ISP protocol */
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]);
161 }else if(rq
->wValue
.bytes
[0] == 0xac && rq
->wValue
.bytes
[1] == 0x80){ /* chip erase */
163 for(addr
= 0; addr
< FLASHEND
+ 1 - 2048; addr
+= SPM_PAGESIZE
) {
164 /* wait and erase page */
166 # ifndef NO_FLASH_WRITE
167 boot_spm_busy_wait();
169 boot_page_erase(addr
);
175 /* ignore all others, return default value == 0 */
177 replyBuffer
[3] = rval
;
179 }else if(rq
->bRequest
== USBASP_FUNC_ENABLEPROG
){
180 /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */
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
;
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
;
195 len
= 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */
197 #if BOOTLOADER_CAN_EXIT
198 }else if(rq
->bRequest
== USBASP_FUNC_DISCONNECT
){
199 requestBootLoaderExit
= 1; /* allow proper shutdown/close of connection */
202 /* ignore: USBASP_FUNC_CONNECT */
207 uchar
usbFunctionWrite(uchar
*data
, uchar len
)
211 DBG1(0x31, (void *)¤tAddress
.l
, 4);
212 if(len
> bytesRemaining
)
213 len
= bytesRemaining
;
214 bytesRemaining
-= len
;
215 isLast
= bytesRemaining
== 0;
216 if(currentRequest
>= USBASP_FUNC_READEEPROM
){
218 for(i
= 0; i
< len
; i
++){
219 eeprom_write_byte((void *)(currentAddress
.w
[0]++), *data
++);
223 for(i
= 0; i
< len
;){
225 if((currentAddress
.w
[0] & (SPM_PAGESIZE
- 1)) == 0){ /* if page start: erase */
227 # ifndef NO_FLASH_WRITE
229 boot_page_erase(CURRENT_ADDRESS
); /* erase page */
231 boot_spm_busy_wait(); /* wait until page is erased */
238 boot_page_fill(CURRENT_ADDRESS
, *(short *)data
);
240 CURRENT_ADDRESS
+= 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
)){
245 #ifndef NO_FLASH_WRITE
247 boot_page_write(CURRENT_ADDRESS
- 2);
249 boot_spm_busy_wait();
256 DBG1(0x35, (void *)¤tAddress
.l
, 4);
261 uchar
usbFunctionRead(uchar
*data
, uchar len
)
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]);
272 *data
= pgm_read_byte((void *)CURRENT_ADDRESS
);
280 /* ------------------------------------------------------------------------ */
282 static void initForUsbConnectivity(void)
287 /* enforce USB re-enumerate: */
288 usbDeviceDisconnect(); /* do this while interrupts are disabled */
289 while(--i
){ /* fake USB disconnect for > 250 ms */
297 int __attribute__((noreturn
)) main(void)
300 wdt_disable(); /* main app may have enabled watchdog */
304 #ifndef NO_FLASH_WRITE
305 GICR
= (1 << IVCE
); /* enable change of interrupt vectors */
306 GICR
= (1 << IVSEL
); /* move interrupts to boot flash section */
308 if(bootLoaderCondition()){
310 initForUsbConnectivity();
313 #if BOOTLOADER_CAN_EXIT
314 if(requestBootLoaderExit
){
321 }while(bootLoaderCondition()); /* main event loop */
326 /* ------------------------------------------------------------------------ */