]> git.zerfleddert.de Git - fnordlicht-mini/blob - firmware/fnordlicht-bootloader/fboot.c
fb06fd7de1e35e7d8d93faa8725aed73c7c226d3
[fnordlicht-mini] / firmware / fnordlicht-bootloader / fboot.c
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 }
Impressum, Datenschutz