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 case it is template for new implementations. It is also documented, other
directories are not. directories are not.
- **`CO_driver.h/.c`** - Microcontroller specific objects for CAN module. - **`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. - **`socketCAN`** - Directory for Linux socketCAN interface.
- **`PIC32`** - Directory for PIC32 devices from Microchip. - **`PIC32`** - Directory for PIC32 devices from Microchip.
- **`PIC24H_dsPIC33F`** - Directory for PIC24H and dsPIC33F 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 * Brief description of the object ends at this dot. Details follow
* here. * here.
*/ */
typedef struct{ typedef struct {
int8_t member1; /**< Short description of the member 1 */ int8_t member1; /**< Short description of the member 1 */
uint16_t member2; /**< Note the '/**<' sequence after the member 2 */ uint16_t member2; /**< Note the '/**<' sequence after the member 2 */
/** Long description of the variable stringMember. More description. */ /** Long description of the variable stringMember. More description. */
char_t stringMember[5]; char_t stringMember[5];
}object1_t; } object1_t;
/** /**
@ -80,15 +80,15 @@ int32_t foo1(
* comment. * comment.
*/ */
if(xy == yz){ /* Comment. '//' comments are not allowed by MISRA-C */ if(xy == yz) { /* Comment. '//' comments are not allowed */
... ...
} }
else{ else {
... ...
} }
switch(zx){ switch(zx) {
case 1:{ case 1: {
... ...
} }
} }

View file

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