vt100: include pgmspace.h as we use PROGMEM macro
[aversive.git] / projects / microb2009 / tests / 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.2 2009-02-20 21:10:01 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 #include <uart.h>
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <util/crc16.h>
35 #include <avr/boot.h>
36
37 #if defined __AVR_ATmega128__
38 #define UARTNUM 0
39 #elif defined __AVR_ATmega2560__
40 #define UARTNUM 3
41 #endif
42
43
44 #define NOECHO
45
46 #ifdef NOECHO
47 #define echo(c) do {} while(0)
48 #else
49 #define echo(c) uart_send(UARTNUM, c)
50 #endif
51
52 static void bootloader_puts(const char *buf)
53 {
54         while (*buf)
55                 uart_send(UARTNUM, *(buf++));
56 }
57
58 static uint32_t bootloader_query_hex(void)
59 {
60         char buf[8];
61         uint8_t i=0;
62         int c;
63
64         memset(buf, 0, sizeof(buf));
65         while (1) {
66                 while ((c=uart_recv_nowait(UARTNUM)) == -1);
67                 if (i >= sizeof(buf) - 1)
68                         continue;
69                 if (c == '\n' || c == '\r') {
70                         echo('\r');
71                         echo('\n');
72                         break;
73                 }
74                 echo(c);
75                 buf[i++] = c;
76         }
77         return strtol(buf, NULL, 16);
78 }
79
80 static void launch_app(void)
81 {
82         bootloader_puts("BOOT\r\n");
83         wait_ms(500);
84         MCUCR = (1 << IVCE);
85         MCUCR = (0 << IVSEL);
86         reset();
87 }
88
89 static void disp_digit(uint8_t x)
90 {
91         if (x < 10)
92                 uart_send(UARTNUM, '0' + x);
93         else
94                 uart_send(UARTNUM, 'a' - 10 + x);
95 }
96
97 static void disp_hex8(uint8_t x)
98 {
99         disp_digit(x>>4);
100         disp_digit(x&0xf);
101 }
102
103 static void disp_hex16(uint16_t x)
104 {
105         disp_hex8(x>>8);
106         disp_hex8(x);
107 }
108
109 static void crc_app(void)
110 {
111         uint32_t start_addr, addr, size;
112         uint8_t c;
113         uint16_t crc = 0xffff;
114
115         bootloader_puts("start addr?\r\n");
116         start_addr = bootloader_query_hex();
117         if (start_addr > FLASHEND)
118                 goto fail;
119         bootloader_puts("size?\r\n");
120         size = bootloader_query_hex();
121         if (start_addr + size > FLASHEND)
122                 goto fail;
123         for (addr=start_addr; addr<start_addr+size; addr++) {
124                 c = pgm_read_byte_far(addr);
125                 crc = _crc_ccitt_update(crc, c);
126         }
127         disp_hex16(crc);
128         return;
129  fail:
130         bootloader_puts("KO");
131 }
132
133 static void prog_page(void)
134 {
135         int c;
136         uint32_t addr;
137         uint16_t i;
138         uint16_t crc = 0xffff;
139         uint8_t buf[SPM_PAGESIZE];
140
141 #define SPM_PAGEMASK ((uint32_t)SPM_PAGESIZE-1)
142         bootloader_puts("addr?\r\n");
143         addr = bootloader_query_hex();
144         if (addr > FLASHEND)
145                 goto fail;
146         /* start_addr must be page aligned */
147         if (addr & SPM_PAGEMASK)
148                 goto fail;
149
150         bootloader_puts("addr ok\r\n");
151
152         /* data is received like the .bin format (which is already in
153          * little endian) */
154         for (i=0; i<SPM_PAGESIZE; i++) {
155                 while ((c=uart_recv_nowait(UARTNUM)) == -1);
156                 crc = _crc_ccitt_update(crc, c);
157                 buf[i] = c;
158         }
159         disp_hex16(crc);
160         bootloader_puts(" (y to valid)\r\n");
161         while ((c=uart_recv_nowait(UARTNUM)) == -1);
162         if (c != 'y')
163                 goto fail;
164
165         /* erase page */
166         eeprom_busy_wait();
167         boot_page_erase(addr);
168         boot_spm_busy_wait();
169         
170         /* Set up little-endian word and fill tmp buf. */
171         for (i=0; i<SPM_PAGESIZE; i+=2) {
172                 uint16_t w = buf[i] + ((uint16_t)(buf[i+1]) << 8);
173                 boot_page_fill(addr + i, w);
174         }
175         
176         boot_page_write(addr);
177         boot_spm_busy_wait();
178         
179         /* Reenable RWW-section again. We need this if we want to jump
180          * back to the application after bootloading. */
181         boot_rww_enable();
182         
183         bootloader_puts("OK");
184         return;
185  fail:
186         bootloader_puts("KO");
187 }
188
189 int main(void)
190 {
191         int c;
192
193         uart_init();
194
195         /* move interrupt vector in bootloader section */
196         MCUCR = (1 << IVCE);
197         MCUCR = (1 << IVSEL);
198
199         sei();
200
201         bootloader_puts("\r\n");
202         while (1) {
203                 bootloader_puts("cmd>");
204                 while ((c=uart_recv_nowait(UARTNUM)) == -1);
205                 if (c == 'x')
206                         launch_app();
207                 else if (c == 'c')
208                         crc_app();
209                 else if (c == 'p')
210                         prog_page();
211                 else
212                         bootloader_puts("bad cmd (p:prog_page c:crc x:exec)");
213                 bootloader_puts("\r\n");
214         }
215         
216         return 0;
217 }