]>
Commit | Line | Data |
---|---|---|
ec1bef8e | 1 | /* vim:ts=4 sts=4 et tw=80 |
2 | * | |
3 | * fnordlichtmini serial bootloader | |
4 | * | |
5 | * for additional information please | |
6 | * see http://lochraster.org/fnordlichtmini | |
7 | * | |
8 | * (c) by Alexander Neumann <alexander@bumpern.de> | |
9 | * Lars Noschinski <lars@public.noschinski.de> | |
10 | * | |
11 | * This program is free software: you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License version 3 as published by | |
13 | * the Free Software Foundation. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
18 | * more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along with | |
21 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | */ | |
23 | ||
24 | #include <avr/io.h> | |
25 | #include <avr/boot.h> | |
26 | #include <avr/wdt.h> | |
27 | #include <avr/pgmspace.h> | |
28 | #include <util/delay.h> | |
29 | #include <util/crc16.h> | |
30 | #include <string.h> | |
31 | #include <stdbool.h> | |
32 | #include "../common/io.h" | |
33 | #include "../common/remote-proto.h" | |
34 | #include "../common/common.h" | |
35 | #include "uart.h" | |
36 | #include "global.h" | |
37 | ||
38 | struct global_t | |
39 | { | |
40 | /* for bus synchronization */ | |
41 | uint8_t synced; | |
42 | uint8_t sync_len; | |
43 | ||
44 | /* own device address */ | |
45 | uint8_t address; | |
46 | ||
47 | /* for crc checking */ | |
48 | bool crc_match; | |
49 | uint8_t delay; | |
50 | ||
51 | /* for enter_application command */ | |
52 | bool request_exit; | |
53 | uint8_t exit_delay; | |
54 | ||
55 | /* for internal message buffer */ | |
56 | uint8_t len; | |
57 | union { | |
58 | uint8_t buf[REMOTE_MSG_LEN]; | |
59 | struct remote_msg_t msg; | |
60 | }; | |
61 | ||
62 | /* length and address for data buffer */ | |
63 | uint16_t data_len; | |
64 | uint8_t *data_address; | |
65 | union { | |
66 | uint8_t data_buf[CONFIG_BOOTLOADER_BUFSIZE]; | |
67 | uint16_t data_buf16[CONFIG_BOOTLOADER_BUFSIZE/2]; | |
68 | }; | |
69 | }; | |
70 | ||
71 | struct global_t __global; | |
72 | #define global (&__global) | |
73 | ||
74 | /* disable watchdog - NEVER CALL DIRECTLY! */ | |
75 | void disable_watchdog(void) \ | |
76 | __attribute__((naked)) \ | |
77 | __attribute__((section(".init3"))); | |
78 | void disable_watchdog(void) | |
79 | { | |
80 | MCUSR = 0; | |
81 | wdt_disable(); | |
82 | } | |
83 | ||
84 | static void (*jump_to_application)(void) = 0; | |
85 | ||
86 | static void start_application(void) __attribute__((noreturn)); | |
87 | static void start_application(void) | |
88 | { | |
89 | /* pwm pins */ | |
90 | P_PORT = 0; | |
91 | P_DDR = 0; | |
92 | ||
93 | /* in pin */ | |
94 | R_PORT = 0; | |
95 | R_DDR = 0; | |
96 | ||
97 | /* uart */ | |
98 | _UBRRH_UART0 = 0; | |
99 | _UBRRL_UART0 = 0; | |
100 | _UCSRA_UART0 = _BV(_TXC_UART0); | |
101 | _UCSRB_UART0 = 0; | |
102 | ||
103 | /* timer1 */ | |
104 | TCCR1B = 0; | |
105 | OCR1A = 0; | |
106 | TCNT1 = 0; | |
107 | _TIFR_TIMER1 = _TIFR_TIMER1; | |
108 | ||
109 | /* move interrupt vectors and start real application */ | |
110 | jump_to_application(); | |
111 | } | |
112 | ||
113 | static void parse_boot_config(struct remote_msg_boot_config_t *msg) | |
114 | { | |
115 | /* remember flash address */ | |
116 | global->data_address = (uint8_t *)msg->start_address; | |
117 | } | |
118 | ||
119 | static void parse_data(struct remote_msg_boot_data_t *msg) | |
120 | { | |
121 | uint8_t len = sizeof(msg->data); | |
122 | if (global->data_len + len > CONFIG_BOOTLOADER_BUFSIZE) | |
123 | len = CONFIG_BOOTLOADER_BUFSIZE - global->data_len; | |
124 | ||
125 | memcpy(&global->data_buf[global->data_len], msg->data, len); | |
126 | global->data_len += len; | |
127 | } | |
128 | ||
129 | static void parse_crc(struct remote_msg_boot_crc_check_t *msg) | |
130 | { | |
131 | /* compute crc16 over buffer */ | |
132 | uint16_t checksum = 0xffff; | |
133 | ||
134 | uint8_t *ptr = &global->data_buf[0]; | |
135 | uint16_t i = msg->len+1; | |
136 | while (--i) | |
137 | checksum = _crc16_update(checksum, *ptr++); | |
138 | ||
139 | if (checksum != msg->checksum) { | |
140 | global->delay = msg->delay; | |
141 | global->crc_match = false; | |
142 | } else | |
143 | global->crc_match = true; | |
144 | } | |
145 | ||
146 | static void parse_crc_flash(struct remote_msg_boot_crc_flash_t *msg) | |
147 | { | |
148 | /* compute crc16 over flash */ | |
149 | uint16_t checksum = 0xffff; | |
150 | ||
151 | uint8_t *address = (uint8_t *)msg->start; | |
152 | uint16_t i = msg->len+1; | |
153 | while (--i) { | |
154 | uint8_t data = pgm_read_byte(address++); | |
155 | checksum = _crc16_update(checksum, data); | |
156 | } | |
157 | ||
158 | if (checksum != msg->checksum) { | |
159 | global->delay = msg->delay; | |
160 | global->crc_match = false; | |
161 | } else | |
162 | global->crc_match = true; | |
163 | } | |
164 | ||
165 | static void flash(void) | |
166 | { | |
167 | /* pull int */ | |
168 | R_DDR |= _BV(INTPIN); | |
169 | ||
170 | uint8_t *addr = global->data_address; | |
171 | uint16_t *data = &global->data_buf16[0]; | |
172 | ||
173 | uint8_t last_page = global->data_len/SPM_PAGESIZE; | |
174 | if (global->data_len % SPM_PAGESIZE) | |
175 | last_page++; | |
176 | ||
177 | for (uint8_t page = 0; page < last_page; page++) | |
178 | { | |
179 | PWM_PIN_ON(PWM_GREEN); | |
180 | ||
181 | /* erase page */ | |
182 | boot_page_erase(addr); | |
183 | boot_spm_busy_wait(); | |
184 | ||
185 | for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) { | |
186 | /* fill internal buffer */ | |
187 | boot_page_fill(addr+i, *data++); | |
188 | boot_spm_busy_wait(); | |
189 | } | |
190 | ||
191 | /* after filling the temp buffer, write the page and wait till we're done */ | |
192 | boot_page_write(addr); | |
193 | boot_spm_busy_wait(); | |
194 | ||
195 | /* re-enable application flash section, so we can read it again */ | |
196 | boot_rww_enable(); | |
197 | ||
198 | PWM_PIN_OFF(PWM_GREEN); | |
199 | addr += SPM_PAGESIZE; | |
200 | } | |
201 | ||
202 | global->data_address = addr; | |
203 | ||
204 | /* release int */ | |
205 | R_DDR &= ~_BV(INTPIN); | |
206 | } | |
207 | ||
208 | static void remote_parse_msg(struct remote_msg_t *msg) | |
209 | { | |
210 | /* verify address */ | |
211 | if (msg->address != global->address && msg->address != REMOTE_ADDR_BROADCAST) | |
212 | return; | |
213 | ||
214 | /* parse command */ | |
215 | switch (msg->cmd) { | |
216 | case REMOTE_CMD_BOOT_CONFIG: parse_boot_config((struct remote_msg_boot_config_t *)msg); | |
217 | break; | |
218 | case REMOTE_CMD_BOOT_INIT: global->data_len = 0; | |
219 | break; | |
220 | case REMOTE_CMD_BOOT_DATA: parse_data((struct remote_msg_boot_data_t *)msg); | |
221 | break; | |
222 | case REMOTE_CMD_CRC_CHECK: parse_crc((struct remote_msg_boot_crc_check_t *)msg); | |
223 | break; | |
224 | case REMOTE_CMD_CRC_FLASH: parse_crc_flash((struct remote_msg_boot_crc_flash_t *)msg); | |
225 | break; | |
226 | case REMOTE_CMD_FLASH: flash(); | |
227 | break; | |
228 | case REMOTE_CMD_ENTER_APP: global->request_exit = true; | |
229 | global->exit_delay = CONFIG_EXIT_DELAY; | |
230 | break; | |
231 | } | |
232 | ||
233 | if (global->delay && global->crc_match == false) { | |
234 | /* pull int to gnd */ | |
235 | R_DDR |= _BV(INTPIN); | |
236 | ||
237 | PWM_PIN_ON(PWM_RED); | |
238 | } | |
239 | } | |
240 | ||
241 | static void remote_poll(void) | |
242 | { | |
243 | /* wait for a byte */ | |
244 | if (uart_receive_complete()) | |
245 | { | |
246 | uint8_t data = uart_getc(); | |
247 | ||
248 | /* check if sync sequence has been received before */ | |
249 | if (global->sync_len == REMOTE_SYNC_LEN) { | |
250 | /* synced, safe address and send next address to following device */ | |
251 | global->address = data; | |
252 | uart_putc(data+1); | |
253 | ||
254 | /* reset buffer */ | |
255 | global->len = 0; | |
256 | ||
257 | /* enable remote command thread */ | |
258 | global->synced = 1; | |
259 | } else { | |
260 | /* just pass through data */ | |
261 | uart_putc(data); | |
262 | ||
263 | /* put data into remote buffer | |
264 | * (just processed if remote.synced == 1) */ | |
265 | if (global->len < sizeof(global->buf)) | |
266 | global->buf[global->len++] = data; | |
267 | } | |
268 | ||
269 | /* remember the number of sync bytes received so far */ | |
270 | if (data == REMOTE_CMD_RESYNC) | |
271 | global->sync_len++; | |
272 | else | |
273 | global->sync_len = 0; | |
274 | } | |
275 | ||
276 | if (global->synced && global->len == REMOTE_MSG_LEN) { | |
277 | remote_parse_msg(&global->msg); | |
278 | global->len = 0; | |
279 | } | |
280 | } | |
281 | ||
282 | static void check_startup(void) | |
283 | { | |
284 | /* check for valid reset vector */ | |
285 | if (pgm_read_word(NULL) == 0xffff) | |
286 | return; | |
287 | ||
288 | /* configure pullup resistor at int pin */ | |
289 | R_PORT |= _BV(INTPIN); | |
290 | ||
291 | /* sleep 100ms (TCNT1 == 2, -> TCNT1L == 2) */ | |
292 | while (TCNT1L < 2); | |
293 | ||
294 | /* if int pin is pulled down, remain in bootloader */ | |
295 | if (!(R_PIN & _BV(INTPIN))) | |
296 | return; | |
297 | ||
298 | /* else cleanup and start application */ | |
299 | start_application(); | |
300 | } | |
301 | ||
302 | int __attribute__ ((noreturn,OS_main)) main(void) | |
303 | { | |
304 | /* initialize timer1, CTC at 50ms, prescaler 1024 */ | |
305 | OCR1A = F_CPU/1024/20; | |
306 | TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10); | |
307 | ||
308 | /* start application if necessary */ | |
309 | check_startup(); | |
310 | ||
311 | /* configure and enable uart */ | |
312 | uart_init(); | |
313 | ||
314 | /* configure outputs */ | |
315 | P_DDR = PWM_CHANNEL_MASK; | |
316 | ||
317 | #ifdef PWM_INVERTED | |
318 | P_PORT = PWM_CHANNEL_MASK; | |
319 | #endif | |
320 | ||
321 | while(1) { | |
322 | remote_poll(); | |
323 | ||
324 | if (_TIFR_TIMER1 & _BV(OCF1A)) { | |
325 | _TIFR_TIMER1 = _BV(OCF1A); | |
326 | ||
327 | static uint8_t c; | |
328 | if (c++ == 20) { | |
329 | /* blink */ | |
330 | PWM_PIN_TOGGLE(PWM_BLUE); | |
331 | c = 0; | |
332 | } | |
333 | ||
334 | /* if int is pulled, decrement delay */ | |
335 | if (R_DDR & _BV(INTPIN)) { | |
336 | if (--global->delay == 0) { | |
337 | /* release int */ | |
338 | R_DDR &= ~_BV(INTPIN); | |
339 | P_PORT &= ~1; | |
340 | } | |
341 | } | |
342 | ||
343 | /* exit (after exit_delay) if requested */ | |
344 | if (global->request_exit && global->exit_delay-- == 0) | |
345 | start_application(); | |
346 | } | |
347 | } | |
348 | } |