1
0
Fork 0

Object Dictionary storage implemented for SocketCAN.

This commit is contained in:
Janez 2015-12-02 13:40:41 +01:00
parent ef15b5e182
commit 9bf4fd860f
8 changed files with 569 additions and 377 deletions

View file

@ -94,7 +94,8 @@ File structure
case it is template for new implementations. It is also documented, other
directories are not.
- **`CO_driver.h/.c`** - Microcontroller specific objects for CAN module.
- **`eeprom.h/.c`** - Functions for storage of Object dictionary.
- **`eeprom.h/.c`** - Functions for storage of Object dictionary, optional.
- **`(helpers.h/.c)`** - Some optional files with specific helper functions.
- **`socketCAN`** - Directory for Linux socketCAN interface.
- **`PIC32`** - Directory for PIC32 devices from Microchip.
- **`PIC24H_dsPIC33F`** - Directory for PIC24H and dsPIC33F devices from Microchip.

View file

@ -46,12 +46,12 @@
* Brief description of the object ends at this dot. Details follow
* here.
*/
typedef struct{
typedef struct {
int8_t member1; /**< Short description of the member 1 */
uint16_t member2; /**< Note the '/**<' sequence after the member 2 */
/** Long description of the variable stringMember. More description. */
char_t stringMember[5];
}object1_t;
} object1_t;
/**
@ -80,15 +80,15 @@ int32_t foo1(
* comment.
*/
if(xy == yz){ /* Comment. '//' comments are not allowed by MISRA-C */
if(xy == yz) { /* Comment. '//' comments are not allowed */
...
}
else{
else {
...
}
switch(zx){
case 1:{
switch(zx) {
case 1: {
...
}
}

View file

@ -40,16 +40,16 @@ void CO_errExit(char* msg);
/* Mainline task (taskMain) ***************************************************/
static struct{
static struct {
int fdTmr; /* file descriptor for taskTmr */
int fdPipe[2]; /* file descriptors for pipe [0]=read, [1]=write */
struct itimerspec tmrSpec;
uint16_t tmr1msPrev;
uint16_t *maxTime;
}taskMain;
} taskMain;
void taskMain_init(int fdEpoll, uint16_t *maxTime){
void taskMain_init(int fdEpoll, uint16_t *maxTime) {
struct epoll_event ev;
int flags;
@ -106,21 +106,21 @@ void taskMain_init(int fdEpoll, uint16_t *maxTime){
}
void taskMain_close(void){
void taskMain_close(void) {
close(taskMain.fdPipe[0]);
close(taskMain.fdPipe[1]);
close(taskMain.fdTmr);
}
bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms) {
bool_t wasProcessed = true;
/* Signal from pipe, consume all bytes. */
if(fd == taskMain.fdPipe[0]){
for(;;){
if(fd == taskMain.fdPipe[0]) {
for(;;) {
char ch;
if(read(taskMain.fdPipe[0], &ch, 1) == -1){
if(read(taskMain.fdPipe[0], &ch, 1) == -1) {
if (errno == EAGAIN)
break; /* No more bytes. */
else
@ -130,17 +130,17 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
}
/* Timer expired. */
else if(fd == taskMain.fdTmr){
else if(fd == taskMain.fdTmr) {
uint64_t tmrExp;
if(read(taskMain.fdTmr, &tmrExp, sizeof(tmrExp)) != sizeof(uint64_t))
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x21200000L | errno);
}
else{
else {
wasProcessed = false;
}
/* Process mainline. */
if(wasProcessed){
if(wasProcessed) {
uint16_t timer1msDiff;
uint16_t timerNext = 50;
@ -149,8 +149,8 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
taskMain.tmr1msPrev = timer1ms;
/* Calculate maximum interval in milliseconds (informative) */
if(taskMain.maxTime != NULL){
if(timer1msDiff > *taskMain.maxTime){
if(taskMain.maxTime != NULL) {
if(timer1msDiff > *taskMain.maxTime) {
*taskMain.maxTime = timer1msDiff;
}
}
@ -171,14 +171,14 @@ bool_t taskMain_process(int fd, CO_NMT_reset_cmd_t *reset, uint16_t timer1ms){
}
void taskMain_cbSignal(void){
void taskMain_cbSignal(void) {
if(write(taskMain.fdPipe[1], "x", 1) == -1)
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x23100000L | errno);
}
/* Realtime task (taskRT) *****************************************************/
static struct{
static struct {
int fdRx0; /* file descriptor for CANrx */
int fdTmr; /* file descriptor for taskTmr */
struct itimerspec tmrSpec;
@ -188,10 +188,10 @@ static struct{
uint16_t *maxTime;
int fdEpoll;
bool_t CANrx_locked;
}taskRT;
} taskRT;
void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime){
void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime) {
struct epoll_event ev;
/* get file descriptors */
@ -234,21 +234,21 @@ void CANrx_taskTmr_init(int fdEpoll, long intervalns, uint16_t *maxTime){
}
void CANrx_taskTmr_close(void){
void CANrx_taskTmr_close(void) {
close(taskRT.fdTmr);
}
bool_t CANrx_taskTmr_process(int fd){
bool_t CANrx_taskTmr_process(int fd) {
bool_t wasProcessed = true;
/* Get received CAN message. */
if(fd == taskRT.fdRx0){
if(fd == taskRT.fdRx0) {
CO_CANrxWait(CO->CANmodule[0]);
}
/* Execute taskTmr */
else if(fd == taskRT.fdTmr){
else if(fd == taskRT.fdTmr) {
uint64_t tmrExp;
/* Wait for timer to expire */
@ -256,17 +256,17 @@ bool_t CANrx_taskTmr_process(int fd){
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x22100000L | errno);
/* Calculate maximum interval in microseconds (informative) */
if(taskRT.maxTime != NULL){
if(taskRT.maxTime != NULL) {
struct timespec tmrMeasure;
if(clock_gettime(CLOCK_MONOTONIC, &tmrMeasure) == -1)
CO_errorReport(CO->em, CO_EM_GENERIC_SOFTWARE_ERROR, CO_EMC_SOFTWARE_INTERNAL, 0x22200000L | errno);
if(tmrMeasure.tv_sec == taskRT.tmrVal->tv_sec){
if(tmrMeasure.tv_sec == taskRT.tmrVal->tv_sec) {
long dt = tmrMeasure.tv_nsec - taskRT.tmrVal->tv_nsec;
dt /= 1000;
dt += taskRT.intervalus;
if(dt > 0xFFFF){
if(dt > 0xFFFF) {
*taskRT.maxTime = 0xFFFF;
}else if(dt > *taskRT.maxTime){
}else if(dt > *taskRT.maxTime) {
*taskRT.maxTime = (uint16_t) dt;
}
}
@ -274,7 +274,7 @@ bool_t CANrx_taskTmr_process(int fd){
/* Calculate next shot for the timer */
taskRT.tmrVal->tv_nsec += taskRT.intervalns;
if(taskRT.tmrVal->tv_nsec >= NSEC_PER_SEC){
if(taskRT.tmrVal->tv_nsec >= NSEC_PER_SEC) {
taskRT.tmrVal->tv_nsec -= NSEC_PER_SEC;
taskRT.tmrVal->tv_sec++;
}
@ -292,7 +292,7 @@ bool_t CANrx_taskTmr_process(int fd){
syncWas = CO_process_SYNC_RPDO(CO, taskRT.intervalus);
/* Re-enable CANrx, if it was disabled by SYNC callback */
if(taskRT.CANrx_locked){
if(taskRT.CANrx_locked) {
struct epoll_event ev;
/* enable epool event */
@ -314,7 +314,7 @@ bool_t CANrx_taskTmr_process(int fd){
CO_UNLOCK_OD();
}
else{
else {
wasProcessed = false;
}
@ -322,8 +322,8 @@ bool_t CANrx_taskTmr_process(int fd){
}
void CANrx_lockCbSync(bool_t syncReceived){
if(syncReceived){
void CANrx_lockCbSync(bool_t syncReceived) {
if(syncReceived) {
struct epoll_event ev;
/* disable epool event */

View file

@ -0,0 +1,379 @@
/*
* CANopen Object Dictionary storage object for Linux SocketCAN.
*
* @file CO_OD_storage.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 "CO_OD_storage.h"
#include "crc16-ccitt.h"
#include <stdio.h>
#include <string.h> /* for memcpy */
#include <stdlib.h> /* for malloc, free */
#define RETURN_SUCCESS 0
#define RETURN_ERROR -1
/******************************************************************************/
CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg) {
CO_OD_storage_t *odStor;
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
odStor = (CO_OD_storage_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) {
/* store parameters */
if(value == 0x65766173UL) {
if(CO_OD_storage_saveSecure(odStor->odAddress, odStor->odSize, odStor->filename) != 0) {
ret = CO_SDO_AB_HW;
}
}
else {
ret = CO_SDO_AB_DATA_TRANSF;
}
}
}
return ret;
}
/******************************************************************************/
CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg) {
CO_OD_storage_t *odStor;
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
odStor = (CO_OD_storage_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) {
/* restore default parameters */
if(value == 0x64616F6CUL) {
if(CO_OD_storage_restoreSecure(odStor->filename) != 0) {
ret = CO_SDO_AB_HW;
}
}
else {
ret = CO_SDO_AB_DATA_TRANSF;
}
}
}
return ret;
}
/******************************************************************************/
int CO_OD_storage_saveSecure(
uint8_t *odAddress,
uint32_t odSize,
char *filename)
{
int ret = RETURN_SUCCESS;
char *filename_old = NULL;
uint16_t CRC = 0;
/* Generate new string with extension '.old' and rename current file to it. */
filename_old = malloc(strlen(filename)+10);
if(filename_old != NULL) {
strcpy(filename_old, filename);
strcat(filename_old, ".old");
remove(filename_old);
if(rename(filename, filename_old) != 0) {
ret = RETURN_ERROR;
}
} else {
ret = RETURN_ERROR;
}
/* Open a new file and write data to it, including CRC. */
if(ret == RETURN_SUCCESS) {
FILE *fp = fopen(filename, "w");
if(fp != NULL) {
CO_LOCK_OD();
fwrite((const void *)odAddress, 1, odSize, fp);
CRC = crc16_ccitt((unsigned char*)odAddress, odSize, 0);
CO_UNLOCK_OD();
fwrite((const void *)&CRC, 1, 2, fp);
fclose(fp);
} else {
ret = RETURN_ERROR;
}
}
/* Verify data */
if(ret == RETURN_SUCCESS) {
void *buf = NULL;
FILE *fp = NULL;
uint32_t cnt = 0;
uint16_t CRC2 = 0;
buf = malloc(odSize + 4);
if(buf != NULL) {
fp = fopen(filename, "r");
if(fp != NULL) {
cnt = fread(buf, 1, odSize, fp);
CRC2 = crc16_ccitt((unsigned char*)buf, odSize, 0);
/* read also two bytes of CRC */
cnt += fread(buf, 1, 4, fp);
fclose(fp);
}
free(buf);
}
/* If size or CRC differs, report error */
if(buf == NULL || fp == NULL || cnt != (odSize + 2) || CRC != CRC2) {
ret = RETURN_ERROR;
}
}
/* In case of error, set back the old file. */
if(ret != RETURN_SUCCESS && filename_old != NULL) {
remove(filename);
rename(filename_old, filename);
}
free(filename_old);
return ret;
}
/******************************************************************************/
int CO_OD_storage_restoreSecure(char *filename) {
int ret = RETURN_SUCCESS;
FILE *fp = NULL;
/* If filename already exists, rename it to '.old'. */
fp = fopen(filename, "r");
if(fp != NULL) {
char *filename_old = NULL;
fclose(fp);
filename_old = malloc(strlen(filename)+10);
if(filename_old != NULL) {
strcpy(filename_old, filename);
strcat(filename_old, ".old");
remove(filename_old);
if(rename(filename, filename_old) != 0) {
ret = RETURN_ERROR;
}
free(filename_old);
}
else {
ret = RETURN_ERROR;
}
}
/* create an empty file and write "-\n" to it. */
if(ret == RETURN_SUCCESS) {
fp = fopen(filename, "w");
if(fp != NULL) {
fputs("-\n", fp);
fclose(fp);
} else {
ret = RETURN_ERROR;
}
}
return ret;
}
/******************************************************************************/
CO_ReturnError_t CO_OD_storage_init(
CO_OD_storage_t *odStor,
uint8_t *odAddress,
uint32_t odSize,
char *filename)
{
CO_ReturnError_t ret = CO_ERROR_NO;
void *buf = NULL;
/* verify arguments */
if(odStor==NULL || odAddress==NULL) {
ret = CO_ERROR_ILLEGAL_ARGUMENT;
}
/* configure object variables and allocate buffer */
if(ret == CO_ERROR_NO) {
odStor->odAddress = odAddress;
odStor->odSize = odSize;
odStor->filename = filename;
odStor->fp = NULL;
odStor->tmr1msPrev = 0;
odStor->lastSavedMs = 0;
buf = malloc(odStor->odSize);
if(buf == NULL) {
ret = CO_ERROR_OUT_OF_MEMORY;
}
}
/* read data from the file and verify CRC */
if(ret == CO_ERROR_NO) {
FILE *fp;
uint32_t cnt = 0;
uint16_t CRC[2];
fp = fopen(odStor->filename, "r");
if(fp) {
cnt = fread(buf, 1, odStor->odSize, fp);
/* read also two bytes of CRC from file */
cnt += fread(&CRC[0], 1, 4, fp);
CRC[1] = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0);
fclose(fp);
}
if(cnt == 2 && *((char*)buf) == '-') {
/* file is empty, default values will be used, no error */
ret = CO_ERROR_NO;
}
else if(cnt != (odStor->odSize + 2)) {
/* file length does not match */
ret = CO_ERROR_DATA_CORRUPT;
}
else if(CRC[0] != CRC[1]) {
/* CRC does not match */
ret = CO_ERROR_CRC;
}
else {
/* no errors, copy data into Object dictionary */
memcpy(odStor->odAddress, buf, odStor->odSize);
}
}
free(buf);
return ret;
}
/******************************************************************************/
CO_ReturnError_t CO_OD_storage_autoSave(
CO_OD_storage_t *odStor,
uint16_t timer1ms,
uint16_t delay)
{
CO_ReturnError_t ret = CO_ERROR_NO;
/* verify arguments */
if(odStor==NULL || odStor->odAddress==NULL) {
ret = CO_ERROR_ILLEGAL_ARGUMENT;
}
/* don't save file more often than delay */
if(odStor->lastSavedMs < delay) {
odStor->lastSavedMs += timer1ms - odStor->tmr1msPrev;
}
else {
void *buf = NULL;
bool_t saveData = false;
/* allocate buffer and open file if necessary */
if(ret == CO_ERROR_NO) {
buf = malloc(odStor->odSize);
if(odStor->fp == NULL) {
odStor->fp = fopen(odStor->filename, "r+");
}
if(buf == NULL || odStor->fp == NULL) {
ret = CO_ERROR_OUT_OF_MEMORY;
}
}
/* read data from the beginning of the file */
if(ret == CO_ERROR_NO) {
uint32_t cnt = 0;
rewind(odStor->fp);
cnt = fread(buf, 1, odStor->odSize, odStor->fp);
if(cnt == 2 && *((char*)buf) == '-') {
/* file is empty, data will be saved. */
saveData = true;
}
else if(cnt == odStor->odSize) {
/* verify, if data differs */
if(memcmp((const void *)buf, (const void *)odStor->odAddress, odStor->odSize) != 0) {
saveData = true;
}
}
else {
/* file length does not match */
ret = CO_ERROR_DATA_CORRUPT;
}
}
/* Save the data to the file only if data differs. */
if(ret == CO_ERROR_NO && saveData) {
uint16_t CRC;
/* copy data to temporary buffer */
memcpy(buf, odStor->odAddress, odStor->odSize);
rewind(odStor->fp);
fwrite((const void *)buf, 1, odStor->odSize, odStor->fp);
/* write also CRC */
CRC = crc16_ccitt((unsigned char*)buf, odStor->odSize, 0);
fwrite((const void *)&CRC, 1, 2, odStor->fp);
fflush(odStor->fp);
odStor->lastSavedMs = 0;
}
free(buf);
}
odStor->tmr1msPrev = timer1ms;
return ret;
}
void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor) {
if(odStor->fp != NULL) {
fclose(odStor->fp);
}
}

View file

@ -0,0 +1,152 @@
/**
* CANopen Object Dictionary storage object for Linux SocketCAN.
*
* @file CO_OD_storage.h
* @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/>.
*/
#ifndef CO_OD_STORAGE_H
#define CO_OD_STORAGE_H
#include "CO_driver.h"
#include "CO_SDO.h"
#include <stdio.h>
/* For documentation see file drvTemplate/CO_OD_storage.h */
/**
* Callbacks for using inside @ref CO_OD_configure() function (for OD objects 1010 and 1011).
*/
CO_SDO_abortCode_t CO_ODF_1010(CO_ODF_arg_t *ODF_arg);
CO_SDO_abortCode_t CO_ODF_1011(CO_ODF_arg_t *ODF_arg);
/**
* Save memory block to a file.
*
* Function renames current file to filename.old, copies contents from odAddress
* to filename, adds two bytes of CRC code. It then verifies the written file and
* in case of errors sets back the old file and returns error.
*
* Function is used with CANopen OD object at index 1010.
*
* @param odAddress Address of the memory block, which will be stored.
* @param odSize Size of the above memory block.
* @param filename Name of the file, where data will be stored.
*
* @return 0 on success, -1 on error.
*/
int CO_OD_storage_saveSecure(
uint8_t *odAddress,
uint32_t odSize,
char *filename);
/**
* Remove OD storage file.
*
* Function renames current file to filename.old, then creates empty file and
* writes two bytes "-\n" to it. When program will start next time, default values
* are used for Object Dictionary. In case of error in renaming to .old it
* keeps the original file and returns error.
*
* Writing data to file is secured with mutex CO_LOCK_OD.
*
* Function is used with CANopen OD object at index 1011.
*
* @param filename Name of the file.
*
* @return 0 on success, -1 on error.
*/
int CO_OD_storage_restoreSecure(char *filename);
/**
* Object Dictionary storage object.
*
* Object is used with CANopen OD objects at index 1010 and 1011.
*/
typedef struct {
uint8_t *odAddress; /**< From CO_OD_storage_init() */
uint32_t odSize; /**< From CO_OD_storage_init() */
char *filename; /**< From CO_OD_storage_init() */
/** If CO_OD_storage_autoSave() is used, file stays opened and fp is stored here. */
FILE *fp;
uint16_t tmr1msPrev; /**< used with CO_OD_storage_autoSave. */
uint32_t lastSavedMs; /**< used with CO_OD_storage_autoSave. */
} CO_OD_storage_t;
/**
* Initialize OD storage object and load data from file.
*
* Called after program startup. Load storage file and copy data to Object
* Dictionary variables.
*
* @param odStor This object will be initialized.
* @param odAddress Address of the memory block from Object dictionary, where data will be copied.
* @param odSize Size of the above memory block.
* @param filename Name of the file, where data are stored.
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt),
* CO_ERROR_CRC (CRC from MBR does not match the CRC of OD_ROM block in file),
* CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed).
*/
CO_ReturnError_t CO_OD_storage_init(
CO_OD_storage_t *odStor,
uint8_t *odAddress,
uint32_t odSize,
char *filename);
/**
* Automatically save memory block if differs from file.
*
* Should be called cyclically by program. It first verifies, if memory block
* differs from file and if it does, it saves it to file with two additional
* CRC bytes. File remains opened.
*
* @param odStor OD storage object.
* @param timer1ms Variable, which must increment each millisecond.
* @param delay Delay (inhibit) time between writes to disk in milliseconds (60000 for example).
*
* @return #CO_ReturnError_t: CO_ERROR_NO, CO_ERROR_DATA_CORRUPT (Data in file corrupt),
* CO_ERROR_ILLEGAL_ARGUMENT or CO_ERROR_OUT_OF_MEMORY (malloc failed).
*/
CO_ReturnError_t CO_OD_storage_autoSave(
CO_OD_storage_t *odStor,
uint16_t timer1ms,
uint16_t delay);
/**
* Closes file opened by CO_OD_storage_autoSave.
*
* @param odStor OD storage object.
*/
void CO_OD_storage_autoSaveClose(CO_OD_storage_t *odStor);
#endif

View file

@ -1,242 +0,0 @@
/*
* 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 <stdio.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){
uint32_t value;
CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
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];
}
}

View file

@ -1,98 +0,0 @@
/*
* Eeprom object for Linux SocketCAN.
*
* @file eeprom.h
* @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/>.
*/
#ifndef EEPROM_H
#define EEPROM_H
/* For documentation see file drvTemplate/eeprom.h */
/*
* Linux specific
*
* Usage of device file system or SRAM for storing non-volatile variables.
*
* Two blocks of CANopen Object Dictionary data are stored as non-volatile:
* OD_EEPROM - Stored is in internal battery powered SRAM from address 0. Data
* are stored automatically on change. No data corruption control
* is made. Data are load on startup.
* OD_ROM - Stored in file named "OD_ROM01.dat". Data integrity is
* verified with CRC.
* Data are stored on special CANopen command - Writing 0x65766173
* into Object dictionary (index 1010, subindex 1). Default values
* are restored after reset, if writing 0x64616F6C into (1011, 1).
* Backup is stored to "OD_ROM01.old".
*/
/* Filename */
#ifndef EE_ROM_FILENAME
#define EE_ROM_FILENAME "OD_ROM01.dat"
#endif
#ifndef EE_ROM_FILENAME_OLD
#define EE_ROM_FILENAME_OLD "OD_ROM01.old"
#endif
/* Eeprom object */
typedef struct{
uint32_t *OD_EEPROMAddress;
uint32_t OD_EEPROMSize;
uint8_t *OD_ROMAddress;
uint32_t OD_ROMSize;
uint32_t *pSRAM; /* SC243 specific: Pointer to start
address of the battery powered SRAM */
uint32_t OD_EEPROMCurrentIndex;
bool_t OD_EEPROMWriteEnable;
}CO_EE_t;
/* First part of eeprom initialization.
* @param SRAMAddress Address of battery powered SRAM memory.
*/
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);
/* Second part of eeprom initialization. */
void CO_EE_init_2(
CO_EE_t *ee,
CO_ReturnError_t eeStatus,
CO_SDO_t *SDO,
CO_EM_t *em);
/* Process eeprom object. */
void CO_EE_process(CO_EE_t *ee);
#endif