243 lines
8.5 KiB
C
243 lines
8.5 KiB
C
/*
|
|
* Eeprom object for Linux SocketCAN.
|
|
*
|
|
* @file eeprom.c
|
|
* @author Janez Paternoster
|
|
* @copyright 2015 Janez Paternoster
|
|
*
|
|
* This file is part of CANopenNode, an opensource CANopen Stack.
|
|
* Project home page is <http://canopennode.sourceforge.net>.
|
|
* For more information on CANopen see <http://www.can-cia.org/>.
|
|
*
|
|
* CANopenNode is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "CO_driver.h"
|
|
#include "CO_SDO.h"
|
|
#include "CO_Emergency.h"
|
|
#include "eeprom.h"
|
|
#include "crc16-ccitt.h"
|
|
|
|
#include <string.h> /* for memcpy */
|
|
#include <stdlib.h> /* for malloc, free */
|
|
|
|
/* Store parameters ***********************************************************/
|
|
static CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg){
|
|
CO_EE_t *ee;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
ee = (CO_EE_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
/* don't change the old value */
|
|
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
|
|
|
if(ODF_arg->subIndex == 1){
|
|
if(value == 0x65766173UL){
|
|
/* store parameters */
|
|
/* rename current file to .old */
|
|
remove(EE_ROM_FILENAME_OLD);
|
|
rename(EE_ROM_FILENAME, EE_ROM_FILENAME_OLD);
|
|
/* open a file */
|
|
FILE *fp = fopen(EE_ROM_FILENAME, "wb");
|
|
if(!fp){
|
|
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
|
return CO_SDO_AB_HW;
|
|
}
|
|
|
|
/* write data to the file */
|
|
fwrite((const void *)ee->OD_ROMAddress, 1, ee->OD_ROMSize, fp);
|
|
/* write CRC to the end of the file */
|
|
uint16_t CRC = crc16_ccitt((unsigned char*)ee->OD_ROMAddress, ee->OD_ROMSize, 0);
|
|
fwrite((const void *)&CRC, 1, 2, fp);
|
|
fclose(fp);
|
|
|
|
/* verify data */
|
|
void *buf = malloc(ee->OD_ROMSize + 4);
|
|
if(buf){
|
|
fp = fopen(EE_ROM_FILENAME, "rb");
|
|
uint32_t cnt = 0;
|
|
uint16_t CRC2 = 0;
|
|
if(fp){
|
|
cnt = fread(buf, 1, ee->OD_ROMSize, fp);
|
|
CRC2 = crc16_ccitt((unsigned char*)buf, ee->OD_ROMSize, 0);
|
|
/* read also two bytes of CRC */
|
|
cnt += fread(buf, 1, 4, fp);
|
|
fclose(fp);
|
|
}
|
|
free(buf);
|
|
if(cnt == (ee->OD_ROMSize + 2) && CRC == CRC2){
|
|
/* write successful */
|
|
return CO_SDO_AB_NONE;
|
|
}
|
|
}
|
|
/* error, set back the old file */
|
|
remove(EE_ROM_FILENAME);
|
|
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
|
|
|
return CO_SDO_AB_HW;
|
|
}
|
|
else
|
|
return CO_SDO_AB_DATA_TRANSF;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Restore default parameters *************************************************/
|
|
static CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg){
|
|
CO_EE_t *ee;
|
|
uint32_t value;
|
|
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
|
|
|
|
ee = (CO_EE_t*) ODF_arg->object;
|
|
value = CO_getUint32(ODF_arg->data);
|
|
|
|
if(!ODF_arg->reading){
|
|
/* don't change the old value */
|
|
CO_memcpy(ODF_arg->data, (const uint8_t*)ODF_arg->ODdataStorage, 4U);
|
|
|
|
if(ODF_arg->subIndex >= 1){
|
|
if(value == 0x64616F6CUL){
|
|
/* restore default parameters */
|
|
/* rename current file to .old, so it no longer exist */
|
|
remove(EE_ROM_FILENAME_OLD);
|
|
rename(EE_ROM_FILENAME, EE_ROM_FILENAME_OLD);
|
|
|
|
/* create an empty file */
|
|
FILE *fp = fopen(EE_ROM_FILENAME, "wt");
|
|
if(!fp){
|
|
rename(EE_ROM_FILENAME_OLD, EE_ROM_FILENAME);
|
|
return CO_SDO_AB_HW;
|
|
}
|
|
/* write one byte '-' to the file */
|
|
fputc('-', fp);
|
|
fclose(fp);
|
|
|
|
return CO_SDO_AB_NONE;
|
|
}
|
|
else
|
|
return CO_SDO_AB_DATA_TRANSF;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
CO_ReturnError_t CO_EE_init_1(
|
|
CO_EE_t *ee,
|
|
uint8_t *SRAMAddress,
|
|
uint8_t *OD_EEPROMAddress,
|
|
uint32_t OD_EEPROMSize,
|
|
uint8_t *OD_ROMAddress,
|
|
uint32_t OD_ROMSize)
|
|
{
|
|
/* verify arguments */
|
|
if(ee==NULL || OD_EEPROMAddress==NULL || OD_ROMAddress==NULL){
|
|
return CO_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
/* configure object variables */
|
|
ee->pSRAM = (uint32_t*)SRAMAddress;
|
|
ee->OD_EEPROMAddress = (uint32_t*) OD_EEPROMAddress;
|
|
ee->OD_EEPROMSize = OD_EEPROMSize / 4;
|
|
ee->OD_ROMAddress = OD_ROMAddress;
|
|
ee->OD_ROMSize = OD_ROMSize;
|
|
ee->OD_EEPROMCurrentIndex = 0;
|
|
ee->OD_EEPROMWriteEnable = false;
|
|
|
|
/* read the CO_OD_EEPROM from SRAM, first verify, if data are OK */
|
|
if(ee->pSRAM == 0) return CO_ERROR_OUT_OF_MEMORY;
|
|
uint32_t firstWordRAM = *(ee->OD_EEPROMAddress);
|
|
uint32_t firstWordEE = *(ee->pSRAM);
|
|
uint32_t lastWordEE = *(ee->pSRAM + ee->OD_EEPROMSize - 1);
|
|
if(firstWordRAM == firstWordEE && firstWordRAM == lastWordEE){
|
|
unsigned int i;
|
|
for(i=0; i<ee->OD_EEPROMSize; i++)
|
|
(ee->OD_EEPROMAddress)[i] = (ee->pSRAM)[i];
|
|
}
|
|
ee->OD_EEPROMWriteEnable = true;
|
|
|
|
/* read the CO_OD_ROM from file and verify CRC */
|
|
void *buf = malloc(ee->OD_ROMSize);
|
|
if(buf){
|
|
CO_ReturnError_t ret = CO_ERROR_NO;
|
|
FILE *fp = fopen(EE_ROM_FILENAME, "rb");
|
|
uint32_t cnt = 0;
|
|
uint16_t CRC[2];
|
|
if(fp){
|
|
cnt = fread(buf, 1, ee->OD_ROMSize, fp);
|
|
/* read also two bytes of CRC from file */
|
|
cnt += fread(&CRC[0], 1, 4, fp);
|
|
CRC[1] = crc16_ccitt((unsigned char*)buf, ee->OD_ROMSize, 0);
|
|
fclose(fp);
|
|
}
|
|
|
|
if(cnt == 1 && *((char*)buf) == '-'){
|
|
ret = CO_ERROR_NO; /* file is empty, default values will be used, no error */
|
|
}
|
|
else if(cnt != (ee->OD_ROMSize + 2)){
|
|
ret = CO_ERROR_DATA_CORRUPT; /* file length does not match */
|
|
}
|
|
else if(CRC[0] != CRC[1]){
|
|
ret = CO_ERROR_CRC; /* CRC does not match */
|
|
}
|
|
else{
|
|
/* no errors, copy data into object dictionary */
|
|
memcpy(ee->OD_ROMAddress, buf, ee->OD_ROMSize);
|
|
}
|
|
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
else return CO_ERROR_OUT_OF_MEMORY;
|
|
|
|
return CO_ERROR_NO;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
void CO_EE_init_2(
|
|
CO_EE_t *ee,
|
|
CO_ReturnError_t eeStatus,
|
|
CO_SDO_t *SDO,
|
|
CO_EM_t *em)
|
|
{
|
|
CO_OD_configure(SDO, OD_H1010_STORE_PARAM_FUNC, CO_ODF_1010, (void*)ee, 0, 0U);
|
|
CO_OD_configure(SDO, OD_H1011_REST_PARAM_FUNC, CO_ODF_1011, (void*)ee, 0, 0U);
|
|
if(eeStatus != CO_ERROR_NO){
|
|
CO_errorReport(em, CO_EM_NON_VOLATILE_MEMORY, CO_EMC_HARDWARE, (uint32_t)eeStatus);
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
void CO_EE_process(CO_EE_t *ee){
|
|
if(ee && ee->OD_EEPROMWriteEnable){
|
|
/* verify next word */
|
|
unsigned int i = ee->OD_EEPROMCurrentIndex;
|
|
if(++i == ee->OD_EEPROMSize) i = 0;
|
|
ee->OD_EEPROMCurrentIndex = i;
|
|
|
|
/* update SRAM */
|
|
(ee->pSRAM)[i] = (ee->OD_EEPROMAddress)[i];
|
|
}
|
|
}
|