1 /* vim:ts=4 sts=4 et tw=80
3 * fnordlichtmini serial bootloader
5 * for additional information please
6 * see http://lochraster.org/fnordlichtmini
8 * (c) by Alexander Neumann <alexander@bumpern.de>
9 * Lars Noschinski <lars@public.noschinski.de>
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.
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
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/>.
27 #include <avr/pgmspace.h>
28 #include <util/delay.h>
29 #include <util/crc16.h>
32 #include "../common/io.h"
33 #include "../common/remote-proto.h"
34 #include "../common/common.h"
40 /* for bus synchronization */
44 /* own device address */
47 /* for crc checking */
51 /* for enter_application command */
55 /* for internal message buffer */
58 uint8_t buf
[REMOTE_MSG_LEN
];
59 struct remote_msg_t msg
;
62 /* length and address for data buffer */
64 uint8_t *data_address
;
66 uint8_t data_buf
[CONFIG_BOOTLOADER_BUFSIZE
];
67 uint16_t data_buf16
[CONFIG_BOOTLOADER_BUFSIZE
/2];
71 struct global_t __global
;
72 #define global (&__global)
74 /* disable watchdog - NEVER CALL DIRECTLY! */
75 void disable_watchdog(void) \
76 __attribute__((naked
)) \
77 __attribute__((section(".init3")));
78 void disable_watchdog(void)
84 static void (*jump_to_application
)(void) = 0;
86 static void start_application(void) __attribute__((noreturn
));
87 static void start_application(void)
100 _UCSRA_UART0
= _BV(_TXC_UART0
);
107 _TIFR_TIMER1
= _TIFR_TIMER1
;
109 /* move interrupt vectors and start real application */
110 jump_to_application();
113 static void parse_boot_config(struct remote_msg_boot_config_t
*msg
)
115 /* remember flash address */
116 global
->data_address
= (uint8_t *)msg
->start_address
;
119 static void parse_data(struct remote_msg_boot_data_t
*msg
)
121 uint8_t len
= sizeof(msg
->data
);
122 if (global
->data_len
+ len
> CONFIG_BOOTLOADER_BUFSIZE
)
123 len
= CONFIG_BOOTLOADER_BUFSIZE
- global
->data_len
;
125 memcpy(&global
->data_buf
[global
->data_len
], msg
->data
, len
);
126 global
->data_len
+= len
;
129 static void parse_crc(struct remote_msg_boot_crc_check_t
*msg
)
131 /* compute crc16 over buffer */
132 uint16_t checksum
= 0xffff;
134 uint8_t *ptr
= &global
->data_buf
[0];
135 uint16_t i
= msg
->len
+1;
137 checksum
= _crc16_update(checksum
, *ptr
++);
139 if (checksum
!= msg
->checksum
) {
140 global
->delay
= msg
->delay
;
141 global
->crc_match
= false;
143 global
->crc_match
= true;
146 static void parse_crc_flash(struct remote_msg_boot_crc_flash_t
*msg
)
148 /* compute crc16 over flash */
149 uint16_t checksum
= 0xffff;
151 uint8_t *address
= (uint8_t *)msg
->start
;
152 uint16_t i
= msg
->len
+1;
154 uint8_t data
= pgm_read_byte(address
++);
155 checksum
= _crc16_update(checksum
, data
);
158 if (checksum
!= msg
->checksum
) {
159 global
->delay
= msg
->delay
;
160 global
->crc_match
= false;
162 global
->crc_match
= true;
165 static void flash(void)
168 R_DDR
|= _BV(INTPIN
);
170 uint8_t *addr
= global
->data_address
;
171 uint16_t *data
= &global
->data_buf16
[0];
173 uint8_t last_page
= global
->data_len
/SPM_PAGESIZE
;
174 if (global
->data_len
% SPM_PAGESIZE
)
177 for (uint8_t page
= 0; page
< last_page
; page
++)
179 PWM_PIN_ON(PWM_GREEN
);
182 boot_page_erase(addr
);
183 boot_spm_busy_wait();
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();
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();
195 /* re-enable application flash section, so we can read it again */
198 PWM_PIN_OFF(PWM_GREEN
);
199 addr
+= SPM_PAGESIZE
;
202 global
->data_address
= addr
;
205 R_DDR
&= ~_BV(INTPIN
);
208 static void remote_parse_msg(struct remote_msg_t
*msg
)
211 if (msg
->address
!= global
->address
&& msg
->address
!= REMOTE_ADDR_BROADCAST
)
216 case REMOTE_CMD_BOOT_CONFIG
: parse_boot_config((struct remote_msg_boot_config_t
*)msg
);
218 case REMOTE_CMD_BOOT_INIT
: global
->data_len
= 0;
220 case REMOTE_CMD_BOOT_DATA
: parse_data((struct remote_msg_boot_data_t
*)msg
);
222 case REMOTE_CMD_CRC_CHECK
: parse_crc((struct remote_msg_boot_crc_check_t
*)msg
);
224 case REMOTE_CMD_CRC_FLASH
: parse_crc_flash((struct remote_msg_boot_crc_flash_t
*)msg
);
226 case REMOTE_CMD_FLASH
: flash();
228 case REMOTE_CMD_ENTER_APP
: global
->request_exit
= true;
229 global
->exit_delay
= CONFIG_EXIT_DELAY
;
233 if (global
->delay
&& global
->crc_match
== false) {
234 /* pull int to gnd */
235 R_DDR
|= _BV(INTPIN
);
241 static void remote_poll(void)
243 /* wait for a byte */
244 if (uart_receive_complete())
246 uint8_t data
= uart_getc();
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
;
257 /* enable remote command thread */
260 /* just pass through data */
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
;
269 /* remember the number of sync bytes received so far */
270 if (data
== REMOTE_CMD_RESYNC
)
273 global
->sync_len
= 0;
276 if (global
->synced
&& global
->len
== REMOTE_MSG_LEN
) {
277 remote_parse_msg(&global
->msg
);
282 static void check_startup(void)
284 /* check for valid reset vector */
285 if (pgm_read_word(NULL
) == 0xffff)
288 /* configure pullup resistor at int pin */
289 R_PORT
|= _BV(INTPIN
);
291 /* sleep 100ms (TCNT1 == 2, -> TCNT1L == 2) */
294 /* if int pin is pulled down, remain in bootloader */
295 if (!(R_PIN
& _BV(INTPIN
)))
298 /* else cleanup and start application */
302 int __attribute__ ((noreturn
,OS_main
)) main(void)
304 /* initialize timer1, CTC at 50ms, prescaler 1024 */
305 OCR1A
= F_CPU
/1024/20;
306 TCCR1B
= _BV(WGM12
) | _BV(CS12
) | _BV(CS10
);
308 /* start application if necessary */
311 /* configure and enable uart */
314 /* configure outputs */
315 P_DDR
= PWM_CHANNEL_MASK
;
318 P_PORT
= PWM_CHANNEL_MASK
;
324 if (_TIFR_TIMER1
& _BV(OCF1A
)) {
325 _TIFR_TIMER1
= _BV(OCF1A
);
330 PWM_PIN_TOGGLE(PWM_BLUE
);
334 /* if int is pulled, decrement delay */
335 if (R_DDR
& _BV(INTPIN
)) {
336 if (--global
->delay
== 0) {
338 R_DDR
&= ~_BV(INTPIN
);
343 /* exit (after exit_delay) if requested */
344 if (global
->request_exit
&& global
->exit_delay
-- == 0)