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