Subversion Repositories HomeAutomation

Rev

Rev 1631 | Rev 1868 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
361 andfri 1
/*
2
 * $Id: bios.c 1641 2010-12-05 22:37:37Z nattgris $
3
 */
1641 nattgris 4
 
361 andfri 5
#include <inttypes.h>
6
#include <avr/io.h>
7
#include <avr/interrupt.h>
8
#include <avr/wdt.h>
9
#include <avr/pgmspace.h>
10
#include <string.h>
11
#include <util/crc16.h>
12
#include <config.h>
13
#include <vectors.h> // Must be included after sfr_defs.h but before any ISR()
14
#include <drivers/can/can.h>
15
#include <flash.h>
16
#include <bios_export.h>
17
#include CAN_DRIVER_H
18
 
769 arune 19
#if !defined( NODE_HW_ID )
20
#error No NODE_HW_ID in bios.inc, set NODE_HW_ID=<GENERATE_HW> and Ill generate it for you
21
#endif
22
 
361 andfri 23
#if defined(__AVR_ATmega8__)
24
#define IVSEL_REG GICR
1468 arune 25
#elif defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__)
361 andfri 26
#define IVSEL_REG MCUCR
27
#else
28
#error AVR device not supported!
29
#endif
30
 
1629 arune 31
/* DEVICETYPE is sent in bios start frame */
32
#if defined(__AVR_ATmega8__)
1631 linlun 33
#define DEVICETYPE 1
1641 nattgris 34
#elif defined(__AVR_ATmega88__)
1631 linlun 35
#define DEVICETYPE 3
1630 arune 36
#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__)
1631 linlun 37
#define DEVICETYPE 4
1629 arune 38
#elif defined(__AVR_ATmega328P__)
1631 linlun 39
#define DEVICETYPE 5
1629 arune 40
#else
41
#error AVR device not supported!
42
#endif
43
 
585 arune 44
#if defined(AUTOSTART)
1641 nattgris 45
#if AUTOSTART == 0
585 arune 46
#error Please choose a higher AUTOSTART value in bios.inc, the nod will cease to work, and you can only reflash node with ISP
47
#endif
48
#if AUTOSTART == 1
49
#error Please choose a higher AUTOSTART value in bios.inc, there might be problems with updating software on node
50
#endif
51
#endif
52
 
361 andfri 53
//---------------------------------------------------------------------------
54
// Private declarations
55
 
56
volatile uint8_t bios_msg_full;
57
Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space
58
extern void __bios_start; // Start of BIOS area in flash, from ld-script.
59
 
769 arune 60
prog_uint32_t hwid = NODE_HW_ID;
61
 
585 arune 62
#if defined(AUTOSTART)
63
uint8_t autostart_cnt;
64
#endif
65
 
1641 nattgris 66
#define BIOS_APP     1
67
#define BIOS_NOAPP   2
68
#define BIOS_PGM     3
69
#define BIOS_END_PGM 4
361 andfri 70
 
71
//---------------------------------------------------------------------------
72
// Private functions
73
 
74
void Can_Process(Can_Message_t* msg) {
75
    if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames.
1641 nattgris 76
 
361 andfri 77
    if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
1641 nattgris 78
 
361 andfri 79
        if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) {
80
            // Reset watchdog as long as time messages from the master node
81
            // are received periodically. If anything hangs the CAN communication
82
            // or the master node is not up, the node will be reset.
83
            wdt_reset();
1641 nattgris 84
            //TODO: Don't care about time right now... Figure out what to do with it.
585 arune 85
#if defined(AUTOSTART)
86
            autostart_cnt++;
87
#endif
361 andfri 88
        }
1641 nattgris 89
 
90
        if (((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE) == CAN_NMT_RESET
91
                && msg->DataLength == 4
92
                && msg->Data.bytes[0] == (hwid&0xff)
93
                && msg->Data.bytes[1] == ((hwid>>8)&0xff)
94
                && msg->Data.bytes[2] == ((hwid>>16)&0xff)
95
                && msg->Data.bytes[3] == ((hwid>>24)&0xff)) {
769 arune 96
            BIOS_Reset();
97
        }
98
        // Copy message to bios buffer if bios is done with the previous message.
99
        // This is also a check to see if bios is still running. When app is
100
        // started, bios_msg_full is never reset to 0 so the check fails and all
1641 nattgris 101
        // NMT communication except reset is ignored.
769 arune 102
        if (!(bios_msg_full)) {
103
            memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
104
            bios_msg_full = 1;
105
        }
361 andfri 106
    } else if (BIOS_CanCallback) {
107
        //printf("Calling app callback.\n");
108
        BIOS_CanCallback(msg);
109
    }
110
}
111
 
563 andfri 112
int main(void) {
361 andfri 113
    void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
114
    uint8_t bios_state;
115
    uint8_t nmt_type;
116
    uint8_t pagebuf[SPM_PAGESIZE];
117
    // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
118
    uint16_t base_addr = 0;
119
    uint16_t offset = 0;
120
    uint16_t addr = 0;
121
    uint8_t len;
122
    uint16_t data;
123
    uint8_t send_msg = 0;
124
    Can_Message_t tx_msg;
125
    Can_Message_t bios_msg;
126
    bios_msg_ptr = &bios_msg;
585 arune 127
 
128
#if defined(AUTOSTART)
129
    autostart_cnt = 0;
130
#endif
131
 
361 andfri 132
    // Enable watchdog timer to protect against an application locking the
1641 nattgris 133
    // node by leaving interrupts disabled.
361 andfri 134
    wdt_enable(WDTO_2S);
1641 nattgris 135
 
361 andfri 136
    // Move interrupt vectors to start of bootloader section and enable interrupts
137
    IVSEL_REG = _BV(IVCE);
138
    IVSEL_REG = _BV(IVSEL);
139
    sei();
1641 nattgris 140
 
361 andfri 141
    if (Can_Init() != CAN_OK) BIOS_Reset();
1641 nattgris 142
 
361 andfri 143
    tx_msg.RemoteFlag = 0;
144
    tx_msg.ExtendedFlag = 1;
145
    tx_msg.Id = CAN_ID_NMT_BIOS_START;
769 arune 146
    tx_msg.DataLength = 8;
147
    tx_msg.Data.bytes[0] = BIOS_VERSION&0xff;
148
    tx_msg.Data.bytes[1] = (BIOS_VERSION>>8)&0xff;
1629 arune 149
    tx_msg.Data.bytes[3] = DEVICETYPE;
769 arune 150
    tx_msg.Data.bytes[4] = hwid&0xff;
151
    tx_msg.Data.bytes[5] = (hwid>>8)&0xff;
152
    tx_msg.Data.bytes[6] = (hwid>>16)&0xff;
153
    tx_msg.Data.bytes[7] = (hwid>>24)&0xff;
361 andfri 154
 
155
    if (pgm_read_word(0) == 0xffff) {
156
        // No application in flash
157
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
158
        tx_msg.Data.bytes[2] = 0;
159
        bios_state = BIOS_NOAPP;
160
    } else {
161
        // Application exists
162
        // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
163
        tx_msg.Data.bytes[2] = 1;
164
        bios_state = BIOS_APP;
165
    }
1641 nattgris 166
 
361 andfri 167
    while (Can_Send(&tx_msg) != CAN_OK);
168
 
169
    tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
1641 nattgris 170
 
361 andfri 171
    // Main loop
172
    while (1) {
173
        // Wait for message
585 arune 174
        while (!bios_msg_full) {
175
#if defined(AUTOSTART)
176
            if (bios_state == BIOS_APP && autostart_cnt == AUTOSTART) {
177
                app_reset();
178
            }
179
#endif
180
        }
1641 nattgris 181
 
361 andfri 182
        nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
1641 nattgris 183
 
361 andfri 184
        switch (bios_state) {
185
        case BIOS_APP:
769 arune 186
            //if (nmt_type == CAN_NMT_START_APP) {
1641 nattgris 187
            if (nmt_type == CAN_NMT_START_APP
188
                    && bios_msg.DataLength == 4
189
                    && bios_msg.Data.bytes[0] == (hwid&0xff)
190
                    && bios_msg.Data.bytes[1] == ((hwid>>8)&0xff)
191
                    && bios_msg.Data.bytes[2] == ((hwid>>16)&0xff)
192
                    && bios_msg.Data.bytes[3] == ((hwid>>24)&0xff)) {
361 andfri 193
                app_reset(); // Will never return
194
            }
195
            // Fall through
196
        case BIOS_NOAPP:
769 arune 197
            //if (nmt_type == CAN_NMT_PGM_START) {
1641 nattgris 198
            if (nmt_type == CAN_NMT_PGM_START
199
                    && bios_msg.DataLength == 8
200
                    && bios_msg.Data.bytes[4] == (hwid&0xff)
201
                    && bios_msg.Data.bytes[5] == ((hwid>>8)&0xff)
202
                    && bios_msg.Data.bytes[6] == ((hwid>>16)&0xff)
203
                    && bios_msg.Data.bytes[7] == ((hwid>>24)&0xff)) {
361 andfri 204
                // Set base address
205
                base_addr = bios_msg.Data.words[0];
206
                flash_init(pagebuf);
207
                //send CAN_NMT_PGM_ACK(offset)
208
                tx_msg.Id = CAN_ID_NMT_PGM_ACK;
209
                tx_msg.Data.words[0] = base_addr;
210
                send_msg = 1;
211
                bios_state = BIOS_PGM;
212
            }
213
            break;
214
        case BIOS_PGM:
215
            // Default to send CAN_NMT_PGM_ACK(offset)
216
            tx_msg.Id = CAN_ID_NMT_PGM_ACK;
1641 nattgris 217
 
361 andfri 218
            if (nmt_type == CAN_NMT_PGM_DATA) {
219
                // Set address = base address + offset.
220
                offset = bios_msg.Data.words[0];
221
                addr = base_addr + offset;
1641 nattgris 222
 
361 andfri 223
                // One of ACK and NACK will be sent, both have offset as data.
224
                tx_msg.Data.words[0] = offset;
225
                send_msg = 1;
226
 
227
                // Flash all data sent, beginning at addr.
228
                len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
229
                uint8_t i;
230
                for (i=1; i<len; i++) { // Skip first word (offset).
1641 nattgris 231
 
361 andfri 232
                    // Abort if trying to write in BIOS area.
233
                    if (addr >= (uint16_t)&__bios_start) {
234
                        // Send CAN_NMT_PGM_NACK(offset).
235
                        tx_msg.Id = CAN_ID_NMT_PGM_NACK;
236
                        break; //for loop
237
                    }
238
 
239
                    data = bios_msg.Data.words[i];
240
                    flash_write_word(addr, data);
241
                    addr += 2;
242
                }
243
                // Adjust addr if an odd number of bytes were sent, for use in crc calc.
1641 nattgris 244
                addr -= bios_msg.DataLength & 1;
361 andfri 245
            }
246
            if (nmt_type == CAN_NMT_PGM_END) {
247
                // Make sure a partly filled page will be flashed.
248
                flash_flush_buffer();
1641 nattgris 249
 
361 andfri 250
                // Calculate crc on written data.
251
                //TODO: This will not work if data is not sent sequentially without
252
                // holes, starting with offset=0. We might want to send a NACK if the
253
                // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
254
                uint16_t loc, crc = 0;
255
                for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2.
256
                    crc = _crc16_update(crc, pgm_read_byte(loc));
257
                }
1641 nattgris 258
 
361 andfri 259
                tx_msg.Data.words[0] = crc;
260
                send_msg = 1;
769 arune 261
                bios_state = BIOS_END_PGM;
361 andfri 262
            }
769 arune 263
            break;
264
        case BIOS_END_PGM:
265
            if (nmt_type == CAN_NMT_PGM_COPY) {
266
                // For BIOS updating over CAN. Upload bios to application area,
267
                // send this message with correct parameters to copy data from
268
                // application to bios area and pray it will come alive again.
1641 nattgris 269
                flash_copy_data(bios_msg.Data.words[0],
270
                                bios_msg.Data.words[1],
769 arune 271
                                bios_msg.Data.words[2]);
272
                // flash_copy_data will never return.
273
            }
361 andfri 274
        }
1641 nattgris 275
 
361 andfri 276
        if (send_msg) {
277
            while (Can_Send(&tx_msg) != CAN_OK);
278
            send_msg = 0;
279
        }
280
 
281
        bios_msg_full = 0; // We're done with this message, ready for the next.
282
    }
283
 
284
    return 0;
285
}