add bootloader
[fpv.git] / bootloader / main.c
1 /*
2  *  Copyright Droids Corporation
3  *  Olivier Matz <zer0@droids-corp.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Revision : $Id: main.c,v 1.4 2009-05-27 20:04:06 zer0 Exp $
20  *
21  */
22
23 /*
24  * A simple bootloader example.
25  */
26
27 #include <aversive.h>
28 #include <aversive/wait.h>
29 #include <aversive/pgmspace.h>
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <util/crc16.h>
34 #include <avr/boot.h>
35
36 #define NOECHO
37
38 #ifdef NOECHO
39 #define echo(c) do {} while(0)
40 #else
41 #define echo(c) uart_send(c)
42 #endif
43
44 #if UART_NUM == 0
45
46 #define UCSRxA UCSR0A
47 #define UCSRxB UCSR0B
48 #define UCSRxC UCSR0C
49 #define RXCx RXC0
50 #define UDRx UDR0
51 #define UDREx UDRE0
52 #define U2Xx U2X0
53 #define RXENx RXEN0
54 #define TXENx TXEN0
55 #define UCSZx0 UCSZ00
56 #define UCSZx1 UCSZ01
57 #define UBRRx UBRR0
58
59 #elif  UART_NUM == 1
60
61 #define UCSRxA UCSR1A
62 #define UCSRxB UCSR1B
63 #define UCSRxC UCSR1C
64 #define RXCx RXC1
65 #define UDRx UDR1
66 #define UDREx UDRE1
67 #define U2Xx U2X1
68 #define RXENx RXEN1
69 #define TXENx TXEN1
70 #define UCSZx0 UCSZ10
71 #define UCSZx1 UCSZ11
72 #define UBRRx UBRR1
73
74 #elif  UART_NUM == 2
75
76 #define UCSRxA UCSR2A
77 #define UCSRxB UCSR2B
78 #define UCSRxC UCSR2C
79 #define RXCx RXC2
80 #define UDRx UDR2
81 #define UDREx UDRE2
82 #define U2Xx U2X2
83 #define RXENx RXEN2
84 #define TXENx TXEN2
85 #define UCSZx0 UCSZ20
86 #define UCSZx1 UCSZ21
87 #define UBRRx UBRR2
88
89 #elif  UART_NUM == 3
90
91 #define UCSRxA UCSR3A
92 #define UCSRxB UCSR3B
93 #define UCSRxC UCSR3C
94 #define RXCx RXC3
95 #define UDRx UDR3
96 #define UDREx UDRE3
97 #define U2Xx U2X3
98 #define RXENx RXEN3
99 #define TXENx TXEN3
100 #define UCSZx0 UCSZ30
101 #define UCSZx1 UCSZ31
102 #define UBRRx UBRR3
103
104 #endif
105
106
107 static char uart_recv(void)
108 {
109         while ( !(UCSRxA & (1<<RXCx)) ) ;
110         return UDRx;
111 }
112
113 static void uart_send(char c)
114 {
115         while ( !( UCSRxA & (1<<UDREx)) ) ;
116         UDRx = c;
117 }
118
119 static void uart_puts(const char *buf)
120 {
121         while (*buf)
122                 uart_send(*(buf++));
123 }
124
125 static int8_t bootloader_query_hex(uint32_t *val)
126 {
127         uint32_t tmp = 0;
128         int c;
129
130         while (1) {
131                 c = uart_recv();
132                 echo(c);
133
134                 if (c == '\n' || c == '\r') {
135                         *val = tmp;
136                         return 0;
137                 }
138                 else if (c >= '0' && c <= '9') {
139                         tmp <<= 4;
140                         tmp += (c - '0');
141                 }
142                 else if (c >= 'a' && c <= 'f') {
143                         tmp <<= 4;
144                         tmp += (c - 'a' + 10);
145                 }
146                 else if (c >= 'A' && c <= 'F') {
147                         tmp <<= 4;
148                         tmp += (c - 'A' + 10);
149                 }
150                 else
151                         return -1;
152         }
153         return 0;
154 }
155
156 /* launch application */
157 static void launch_app(void)
158 {
159         uart_puts("Boot...");
160         MCUCR = (1 << IVCE);
161         MCUCR = (0 << IVSEL);
162         reset();
163 }
164
165 static void disp_digit(uint8_t x)
166 {
167         if (x < 10)
168                 x += '0';
169         else
170                 x += 'a' - 10 ;
171         uart_send(x);
172 }
173
174 static void disp_hex8(uint8_t x)
175 {
176         disp_digit(x>>4);
177         disp_digit(x&0xf);
178 }
179
180 static void disp_hex16(uint16_t x)
181 {
182         disp_hex8(x>>8);
183         disp_hex8(x);
184 }
185
186 static void disp_hex32(uint32_t x)
187 {
188         disp_hex16(x>>16);
189         disp_hex16(x);
190 }
191
192 static void crc_app(void)
193 {
194         uint32_t start_addr, addr, size;
195         uint8_t c;
196         uint16_t crc = 0xffff;
197         uint16_t sum = 0;
198
199         uart_puts("addr?\r\n");
200         if (bootloader_query_hex(&start_addr))
201                 goto fail;
202         if (start_addr > FLASHEND)
203                 goto fail;
204         uart_puts("size?\r\n");
205         if (bootloader_query_hex(&size))
206                 goto fail;
207         if (start_addr + size > FLASHEND)
208                 goto fail;
209         for (addr=start_addr; addr<start_addr+size; addr++) {
210 #if 0
211                 /* ignore the 2nd page, it contains microb infos */
212                 if (addr >= 256 && addr < 512)
213                         continue;
214 #endif
215                 c = pgm_read_byte_far(addr);
216                 crc = _crc_ccitt_update(crc, c);
217                 sum += c;
218         }
219         disp_hex16(crc);
220         disp_hex16(sum);
221         return;
222  fail:
223         uart_puts("KO");
224 }
225
226 static void read32(void)
227 {
228         uint32_t start_addr, val = 0;
229         uint8_t c, i;
230
231         uart_puts("addr?\r\n");
232         if (bootloader_query_hex(&start_addr))
233                 goto fail;
234         if (start_addr > FLASHEND)
235                 goto fail;
236         for (i=0; i<4; i++) {
237                 c = pgm_read_byte_far(start_addr+i);
238                 val <<= 8;
239                 val |= c;
240         }
241         disp_hex32(val);
242         return;
243  fail:
244         uart_puts("KO");
245 }
246
247 static void prog_page(void)
248 {
249         int c;
250         uint32_t addr;
251         uint16_t i;
252         uint16_t crc = 0xffff;
253         uint16_t sum = 0;
254         uint8_t buf[SPM_PAGESIZE];
255
256 #define SPM_PAGEMASK ((uint32_t)SPM_PAGESIZE-1)
257         uart_puts("addr?\r\n");
258         if (bootloader_query_hex(&addr))
259                 goto fail;
260         if (addr > FLASHEND)
261                 goto fail;
262         /* start_addr must be page aligned */
263         if (addr & SPM_PAGEMASK)
264                 goto fail;
265
266         uart_puts("ok\r\n");
267
268
269         /* data is received like the .bin format (which is already in
270          * little endian) */
271         for (i=0; i<SPM_PAGESIZE; i++) {
272                 c = uart_recv();
273                 crc = _crc_ccitt_update(crc, c);
274                 sum += c;
275                 buf[i] = c;
276         }
277         disp_hex16(crc);
278         disp_hex16(sum);
279         uart_puts(" (y?)\r\n");
280         c = uart_recv();
281         if (c != 'y')
282                 goto fail;
283
284         /* erase page */
285         eeprom_busy_wait();
286         boot_page_erase(addr);
287         boot_spm_busy_wait();
288
289         /* Set up little-endian word and fill tmp buf. */
290         for (i=0; i<SPM_PAGESIZE; i+=2) {
291                 uint16_t w = buf[i] + ((uint16_t)(buf[i+1]) << 8);
292                 boot_page_fill(addr + i, w);
293         }
294
295         boot_page_write(addr);
296         boot_spm_busy_wait();
297
298         /* Reenable RWW-section again. We need this if we want to jump
299          * back to the application after bootloading. */
300         boot_rww_enable();
301
302         uart_puts("OK");
303         return;
304  fail:
305         uart_puts("KO");
306 }
307
308 int main(void)
309 {
310         int c;
311         uint32_t i=0;
312
313         UCSRxA = _BV(U2Xx);
314         UCSRxB = _BV(RXENx) | _BV(TXENx);
315         UCSRxC = _BV(UCSZx1) | _BV(UCSZx0); /* 8 bits no parity 1 stop */
316         UBRRx = 25; /* 57600 at 12 Mhz */
317
318         /* move interrupt vector in bootloader section */
319         MCUCR = (1 << IVCE);
320         MCUCR = (1 << IVSEL);
321
322         sei();
323
324         uart_puts("\r\ncmd> ");
325
326         /* timeout */
327         while ( !(UCSRxA & (1<<RXCx)) ) {
328                 i++;
329                 if (i>1000000) /* wait about 1 sec */
330                         launch_app();
331         }
332
333         while (1) {
334                 uart_puts("\r\ncmd> ");
335                 c = uart_recv();
336                 if (c == 'x')
337                         launch_app();
338                 else if (c == 'c')
339                         crc_app();
340                 else if (c == 'p')
341                         prog_page();
342                 else if (c == 'd')
343                         read32();
344                 else
345                         uart_puts("p:prog_page c:crc x:exec d:dump32");
346         }
347
348         return 0;
349 }