#include <alchemy/event.h>
#include <alchemy/mutex.h>
#include <rtdm/spi_apf28_rtdm_ioctl.h>
#include <xenomai/init.h>
/* SPI configurable parameters */
/* --------------------------- */
#define TEST_SPI_CLK_PHASE RTDM_SPI_CLKPHASE_LOW
#define TEST_SPI_CLK_POLARITY RTDM_SPI_CLKPOLARITY_LOW
#define TEST_SPI_SPEED 20000000
#define TEST_SPI_BITS_PER_WORD RTDM_SPI_8BPW
#define TEST_SPI_CHIPSELECT1 RTDM_SPI_SS0
#define TEST_SPI_CHIPSELECT2 RTDM_SPI_SS0
/* Use the DMA from transfer > 4 bytes */
#define TEST_SPI_DMA_BYTES_MIN 4
#define NVRAM_SIZE 0x8000
#define RETAIN_BUFFER_SIZE (NVRAM_SIZE - IDLEN)
static RT_TASK RETAIN_task;
static RT_MUTEX RETAIN_Mutex;
static RT_EVENT RETAIN_Event;
/* Data protected by mutex */
unsigned char cmd[CMDSIZE];
unsigned char vars[RETAIN_BUFFER_SIZE];
static unsigned short actual_size;
static int retain_busy = 0;
/* 0 : PLC can update. No SPI transmission undergoing
1 : PLC cannot update. SPI transmission undergoing */
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static uint8_t rcmd[] = {
int SPI_Read(void *buf, unsigned int len){
if (ioctl(fd_spi, RTDM_SPI_LOCK_CS_SET, &uint) < 0) {
printf("Failed to lock CS\n");
status = write(fd_spi, rcmd, ARRAY_SIZE(rcmd));
fprintf(stderr, "Failed to send read command. ERR : %d\n", status);
if (ioctl(fd_spi, RTDM_SPI_LOCK_CS_SET, &uint) < 0) {
printf("Failed to unlock CS\n");
size = read(fd_spi, buf, len);
fprintf(stderr, "Failed to read %d bytes on the SPI bus. size : %d\n",
int SPI_Write(void *buf, unsigned int len){
status = write(fd_spi, buf, len);
fprintf(stderr, "Failed to write memory. ERR : %d\n", status);
static void SPI_init(void)
/* Open the SPI RTDM device */
if ((fd_spi = open("/dev/rtdm/spi_apf28_rtdm", 0)) < 0) {
printf("rt_spi_open error\n");
uint = TEST_SPI_CLK_PHASE;
if (ioctl(fd_spi, RTDM_SPI_CLKPHASE_SET, &uint) < 0) {
printf("Failed to configure the clock phase\n");
if (ioctl(fd_spi, RTDM_SPI_CLKPHASE_GET, &uint) < 0) {
printf("Failed to get the clock phase\n");
uint = TEST_SPI_CLK_POLARITY;
if (ioctl(fd_spi, RTDM_SPI_CLKPOLARITY_SET, &uint) < 0) {
printf("Failed to configure the clock polarity\n");
if (ioctl(fd_spi, RTDM_SPI_CLKPOLARITY_GET, &uint) < 0) {
printf("Failed to get the clock polarity\n");
if (ioctl(fd_spi, RTDM_SPI_FREQ_SET, &ulong) < 0) {
printf("Failed to configure the frequency\n");
if (ioctl(fd_spi, RTDM_SPI_FREQ_GET, &ulong) < 0) {
printf("Failed to get the frequency\n");
uint = TEST_SPI_BITS_PER_WORD;
if (ioctl(fd_spi, RTDM_SPI_BITSPERWORD_SET, &uint) < 0) {
printf("Failed to configure the BPW\n");
if (ioctl(fd_spi, RTDM_SPI_BITSPERWORD_GET, &uint) < 0) {
printf("Failed to get the BPW\n");
uint = TEST_SPI_CHIPSELECT1;
if (ioctl(fd_spi, RTDM_SPI_CHIPSELECT_SET, &uint) < 0) {
printf("Failed to configure the chip select\n");
if (ioctl(fd_spi, RTDM_SPI_CHIPSELECT_GET, &uint) < 0) {
printf("Failed to get the chip select\n");
/* DMA usage definition */
uint = TEST_SPI_DMA_BYTES_MIN;
if (ioctl(fd_spi, RTDM_SPI_DMA_SET, &uint) < 0) {
printf("Failed to configure the DMA threshold\n");
if (ioctl(fd_spi, RTDM_SPI_DMA_GET, &uint) < 0) {
printf("Failed to get the DMA threshold\n");
/* Prepare for future write commands */
/* Set Sequencial access mode */
// status = write(fd_spi, mbuf, ARRAY_SIZE(mbuf));
printf("Failed to change mode. ERR : %d\n", status);
/* Read the whole NVRAM at start */
SPI_Read(&priv.buf, NVRAM_SIZE);
void RETAIN_task_proc(void *arg)
while (!rt_event_wait(&RETAIN_Event, 1, &msk, EV_ANY, TM_INFINITE)) {
if(rt_mutex_acquire(&RETAIN_Mutex, TM_INFINITE)) return;
SPI_Write(&priv, CMDSIZE + actual_size + IDLEN);
if(rt_event_clear(&RETAIN_Event, 1, NULL)) return;
if(rt_mutex_release(&RETAIN_Mutex)) return;
void InitRetain(void) /* TODO return error and care */
if(rt_event_create (&RETAIN_Event, "RETAIN_Event", 0, 0))
if(rt_mutex_create (&RETAIN_Mutex, "RETAIN_Mutex"))
if(rt_task_create(&RETAIN_task, "RETAIN_task", 0, 50, T_JOINABLE))
SPI_init(); /*TODO if error */
if(rt_task_start(&RETAIN_task, &RETAIN_task_proc, NULL))
while(!NVRAM_Done && count--){
printf("NVRAM init timeout\n");
rt_task_delete(&RETAIN_task);
rt_task_join(&RETAIN_task);
rt_mutex_delete(&RETAIN_Mutex);
rt_event_delete(&RETAIN_Event);
void ValidateRetainBuffer(void)
memcpy(priv.buf.id, PLC_ID, IDLEN);
rt_mutex_release(&RETAIN_Mutex);
rt_event_signal(&RETAIN_Event, 1);
void InValidateRetainBuffer(void)
int ret = rt_mutex_acquire(&RETAIN_Mutex, TM_NONBLOCK);
/* mutex was unavailable */
/* invalidate that buffer */
bzero(&priv.buf.id[0], IDLEN);
//printf("invalidate retain\n");
int CheckRetainBuffer(void)
/* compare RETAIN ID buffer with MD5 */
/* return true if identical */
res = memcmp(&priv.buf.id[0], PLC_ID, IDLEN) == 0;
void Retain(unsigned int offset, unsigned int count, void *p)
if(!retain_busy && offset + count < RETAIN_BUFFER_SIZE){
/* write in RETAIN buffer at offset*/
actual_size = offset + count;
memcpy(&priv.buf.vars[offset], p, count);
void Remind(unsigned int offset, unsigned int count, void *p)
if(!retain_busy && offset + count < RETAIN_BUFFER_SIZE)
/* read at offset in RETAIN buffer */
memcpy(p, &priv.buf.vars[offset], count);