Subversion Repositories HomeAutomation

Rev

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

  1. /*
  2.  * $Id: bios.c 2032 2012-06-27 23:08:42Z pengi $
  3.  */
  4.  
  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.  
  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.  
  23. #if defined(__AVR_ATmega8__)
  24. #define IVSEL_REG GICR
  25. #elif defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__)
  26. #define IVSEL_REG MCUCR
  27. #else
  28. #error AVR device not supported!
  29. #endif
  30.  
  31. /* DEVICETYPE is sent in bios start frame */
  32. #if defined(__AVR_ATmega8__)
  33. #define DEVICETYPE 1
  34. #elif defined(__AVR_ATmega88__)
  35. #define DEVICETYPE 3
  36. #elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__)
  37. #define DEVICETYPE 4
  38. #elif defined(__AVR_ATmega328P__)
  39. #define DEVICETYPE 5
  40. #else
  41. #error AVR device not supported!
  42. #endif
  43.  
  44. #if defined(AUTOSTART)
  45. #if AUTOSTART == 0
  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.  
  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 char __bios_start; // Start of BIOS area in flash, from ld-script.
  59.  
  60. prog_uint32_t hwid = NODE_HW_ID;
  61.  
  62. #if defined(AUTOSTART)
  63. uint8_t autostart_cnt;
  64. #endif
  65.  
  66. #define BIOS_APP     1
  67. #define BIOS_NOAPP   2
  68. #define BIOS_PGM     3
  69. #define BIOS_END_PGM 4
  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.
  76.  
  77.     if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
  78.  
  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();
  84.             //TODO: Don't care about time right now... Figure out what to do with it.
  85. #if defined(AUTOSTART)
  86.             autostart_cnt++;
  87. #endif
  88.         }
  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)) {
  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
  101.         // NMT communication except reset is ignored.
  102.         if (!(bios_msg_full)) {
  103.             memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
  104.             bios_msg_full = 1;
  105.         }
  106.     }
  107.     if (BIOS_CanCallback) {
  108.         //printf("Calling app callback.\n");
  109.         BIOS_CanCallback(msg);
  110.     }
  111. }
  112.  
  113. int main(void) {
  114.     void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
  115.     uint8_t bios_state;
  116.     uint8_t nmt_type;
  117.     uint8_t pagebuf[SPM_PAGESIZE];
  118.     // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
  119.     uint16_t base_addr = 0;
  120.     uint16_t offset = 0;
  121.     uint16_t addr = 0;
  122.     uint8_t len;
  123.     uint16_t data;
  124.     uint8_t send_msg = 0;
  125.     Can_Message_t tx_msg;
  126.     Can_Message_t bios_msg;
  127.     bios_msg_ptr = &bios_msg;
  128.  
  129. #if defined(AUTOSTART)
  130.     autostart_cnt = 0;
  131. #endif
  132.  
  133.     // Enable watchdog timer to protect against an application locking the
  134.     // node by leaving interrupts disabled.
  135.     wdt_enable(WDTO_2S);
  136.  
  137.     // Move interrupt vectors to start of bootloader section and enable interrupts
  138.     IVSEL_REG = _BV(IVCE);
  139.     IVSEL_REG = _BV(IVSEL);
  140.     sei();
  141.  
  142.     if (Can_Init() != CAN_OK) BIOS_Reset();
  143. #if defined(INITDDRB)
  144.     DDRB=INITDDRB;
  145. #endif
  146. #if defined(INITDDRC)
  147.     DDRC=INITDDRC;
  148. #endif
  149. #if defined(INITDDRD)
  150.     DDRD=INITDDRD;
  151. #endif
  152. #if defined(INITPORTB)
  153.     PORTB=INITPORTB;
  154. #endif
  155. #if defined(INITPORTC)
  156.     PORTC=INITPORTC;
  157. #endif
  158. #if defined(INITPORTD)
  159.     PORTD=INITPORTD;
  160. #endif
  161. #if defined(INITPINB)
  162.     PINB=INITPINB;
  163. #endif
  164. #if defined(INITPINC)
  165.     PINC=INITPINC;
  166. #endif
  167. #if defined(INITPIND)
  168.     PIND=INITPIND;
  169. #endif
  170.  
  171.  
  172.     tx_msg.RemoteFlag = 0;
  173.     tx_msg.ExtendedFlag = 1;
  174.     tx_msg.Id = CAN_ID_NMT_BIOS_START;
  175.     tx_msg.DataLength = 8;
  176.     tx_msg.Data.bytes[0] = BIOS_VERSION&0xff;
  177.     tx_msg.Data.bytes[1] = (BIOS_VERSION>>8)&0xff;
  178.     tx_msg.Data.bytes[3] = DEVICETYPE;
  179.     tx_msg.Data.bytes[4] = hwid&0xff;
  180.     tx_msg.Data.bytes[5] = (hwid>>8)&0xff;
  181.     tx_msg.Data.bytes[6] = (hwid>>16)&0xff;
  182.     tx_msg.Data.bytes[7] = (hwid>>24)&0xff;
  183.  
  184.     if (pgm_read_word(0) == 0xffff) {
  185.         // No application in flash
  186.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
  187.         tx_msg.Data.bytes[2] = 0;
  188.         bios_state = BIOS_NOAPP;
  189.     } else {
  190.         // Application exists
  191.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
  192.         tx_msg.Data.bytes[2] = 1;
  193.         bios_state = BIOS_APP;
  194.     }
  195.  
  196.     while (Can_Send(&tx_msg) != CAN_OK);
  197.  
  198.     tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
  199.  
  200.     // Main loop
  201.     while (1) {
  202.         // Wait for message
  203.         while (!bios_msg_full) {
  204. #if defined(AUTOSTART)
  205.             if (bios_state == BIOS_APP && autostart_cnt == AUTOSTART) {
  206.                 app_reset();
  207.             }
  208. #endif
  209.         }
  210.  
  211.         nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
  212.  
  213.         switch (bios_state) {
  214.         case BIOS_APP:
  215.             //if (nmt_type == CAN_NMT_START_APP) {
  216.             if (nmt_type == CAN_NMT_START_APP
  217.                     && bios_msg.DataLength == 4
  218.                     && bios_msg.Data.bytes[0] == (hwid&0xff)
  219.                     && bios_msg.Data.bytes[1] == ((hwid>>8)&0xff)
  220.                     && bios_msg.Data.bytes[2] == ((hwid>>16)&0xff)
  221.                     && bios_msg.Data.bytes[3] == ((hwid>>24)&0xff)) {
  222.                 app_reset(); // Will never return
  223.             }
  224.             // Fall through
  225.         case BIOS_NOAPP:
  226.             //if (nmt_type == CAN_NMT_PGM_START) {
  227.             if (nmt_type == CAN_NMT_PGM_START
  228.                     && bios_msg.DataLength == 8
  229.                     && bios_msg.Data.bytes[4] == (hwid&0xff)
  230.                     && bios_msg.Data.bytes[5] == ((hwid>>8)&0xff)
  231.                     && bios_msg.Data.bytes[6] == ((hwid>>16)&0xff)
  232.                     && bios_msg.Data.bytes[7] == ((hwid>>24)&0xff)) {
  233.                 // Set base address
  234.                 base_addr = bios_msg.Data.words[0];
  235.                 flash_init(pagebuf);
  236.                 //send CAN_NMT_PGM_ACK(offset)
  237.                 tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  238.                 tx_msg.Data.words[0] = base_addr;
  239.                 send_msg = 1;
  240.                 bios_state = BIOS_PGM;
  241.             }
  242.             break;
  243.         case BIOS_PGM:
  244.             // Default to send CAN_NMT_PGM_ACK(offset)
  245.             tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  246.  
  247.             if (nmt_type == CAN_NMT_PGM_DATA) {
  248.                 // Set address = base address + offset.
  249.                 offset = bios_msg.Data.words[0];
  250.                 addr = base_addr + offset;
  251.  
  252.                 // One of ACK and NACK will be sent, both have offset as data.
  253.                 tx_msg.Data.words[0] = offset;
  254.                 send_msg = 1;
  255.  
  256.                 // Flash all data sent, beginning at addr.
  257.                 len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
  258.                 uint8_t i;
  259.                 for (i=1; i<len; i++) { // Skip first word (offset).
  260.  
  261.                     // Abort if trying to write in BIOS area.
  262.                     if (addr >= (uint16_t)&__bios_start) {
  263.                         // Send CAN_NMT_PGM_NACK(offset).
  264.                         tx_msg.Id = CAN_ID_NMT_PGM_NACK;
  265.                         break; //for loop
  266.                     }
  267.  
  268.                     data = bios_msg.Data.words[i];
  269.                     flash_write_word(addr, data);
  270.                     addr += 2;
  271.                 }
  272.                 // Adjust addr if an odd number of bytes were sent, for use in crc calc.
  273.                 addr -= bios_msg.DataLength & 1;
  274.             }
  275.             if (nmt_type == CAN_NMT_PGM_END) {
  276.                 // Make sure a partly filled page will be flashed.
  277.                 flash_flush_buffer();
  278.  
  279.                 // Calculate crc on written data.
  280.                 //TODO: This will not work if data is not sent sequentially without
  281.                 // holes, starting with offset=0. We might want to send a NACK if the
  282.                 // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
  283.                 uint16_t loc, crc = 0;
  284.                 for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2.
  285.                     crc = _crc16_update(crc, pgm_read_byte(loc));
  286.                 }
  287.  
  288.                 tx_msg.Data.words[0] = crc;
  289.                 send_msg = 1;
  290.                 bios_state = BIOS_END_PGM;
  291.             }
  292.             break;
  293.         case BIOS_END_PGM:
  294.             if (nmt_type == CAN_NMT_PGM_COPY) {
  295.                 // For BIOS updating over CAN. Upload bios to application area,
  296.                 // send this message with correct parameters to copy data from
  297.                 // application to bios area and pray it will come alive again.
  298.                 flash_copy_data(bios_msg.Data.words[0],
  299.                                 bios_msg.Data.words[1],
  300.                                 bios_msg.Data.words[2]);
  301.                 // flash_copy_data will never return.
  302.             }
  303.         }
  304.  
  305.         if (send_msg) {
  306.             while (Can_Send(&tx_msg) != CAN_OK);
  307.             send_msg = 0;
  308.         }
  309.  
  310.         bios_msg_full = 0; // We're done with this message, ready for the next.
  311.     }
  312.  
  313.     return 0;
  314. }
  315.