Commit f2aa4688 authored by Federico Vaga's avatar Federico Vaga

common:rt: introduce library for RealTime app

Features:
    - protocol between host and RT
    - action dispatcher
    - export variable and structures to the host
    - generic action like ping and version
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent f86de35d
...@@ -26,6 +26,9 @@ OBJS += $(WRNC)/applications/common/rt/wrn-crt0.o ...@@ -26,6 +26,9 @@ OBJS += $(WRNC)/applications/common/rt/wrn-crt0.o
OBJS += $(WRNC)/applications/common/rt/vsprintf-xint.o OBJS += $(WRNC)/applications/common/rt/vsprintf-xint.o
OBJS += $(WRNC)/applications/common/rt/printf.o OBJS += $(WRNC)/applications/common/rt/printf.o
OBJS += $(WRNC)/applications/common/rt/rt-common.o OBJS += $(WRNC)/applications/common/rt/rt-common.o
ifdef RT_USE_LIBRT
OBJS += $(WRNC)/applications/common/rt/librt.o
endif
LDSCRIPT = $(WRNC)/applications/common/rt/wrnode.ld LDSCRIPT = $(WRNC)/applications/common/rt/wrnode.ld
......
/*
* Copyright (C) 2015 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 3
*/
#include <stdint.h>
#include <errno.h>
#include <librt.h>
static action_t **rt_actions; /**< Exported actions */
static struct rt_variable *rt_variables; /**< Exported variables list */
static struct rt_structure *rt_structures; /**< Exported structures list */
static unsigned int rt_variables_count, rt_structures_count, rt_action_count;
const struct wrnc_rt_version version = {0, RT_APPLICATION_ID, 0, GIT_VERSION};
void rt_variable_export(struct rt_variable *variables, unsigned int count)
{
rt_variables_count = count;
rt_variables = variables;
}
void rt_structure_export(struct rt_structure *structures, unsigned int count)
{
rt_structures_count = count;
rt_structures = structures;
}
void rt_action_export(action_t **actions, unsigned int count)
{
rt_actions = actions;
rt_action_count = count;
}
/**
* it sets a structure coming from the host
*/
int rt_structure_setter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
unsigned int i, offset = 0, index, size;
uint32_t *din = pin;
while (offset < hin->len) {
#ifdef LIBRT_DEBUG
pp_printf("%s: offset %d/%d\n", __func__, offset, hin->len);
#endif
index = din[offset++];
size = din[offset++];
#ifdef LIBRT_DEBUG
pp_printf("%s Type %d Len %d Addr 0x%x\n", __func__,
index, size, rt_structures[index].struct_ptr);
delay(100000);
#endif
if (rt_structures[index].len == size) {
memcpy(rt_structures[index].struct_ptr,
&din[offset], size);
}
#ifdef LIBRT_ERROR
else {
pp_printf("%s:%d structure len not correct %d != %d\n",
__func__, __LINE__,
rt_structures[index].len, size);
}
#endif
offset += (size / 4); /* Next TLV record */
}
return 0;
}
/**
* it copies a structure to the host
*/
int rt_structure_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
unsigned int i, offset = 0, index, size;
uint32_t *din = pin;
uint32_t *dout = pout;
hout->msg_id = RT_ACTION_SEND_STRUCT_GET;
while (offset < hin->len) {
#ifdef LIBRT_DEBUG
pp_printf("%s: offset %d/%d\n", __func__, offset, hin->len);
#endif
index = din[offset];
dout[offset++] = index;
size = din[offset];
dout[offset++] = size;
#ifdef LIBRT_DEBUG
pp_printf("%s Type %d Len %d Addr 0x%x\n", __func__,
index, size, rt_structures[index].struct_ptr);
delay(100000);
#endif
if (rt_structures[index].len == size) {
memcpy(&dout[offset], rt_structures[index].struct_ptr,
size);
}
#ifdef LIBRT_ERROR
else {
pp_printf("%s: structure len not correct %d != %d\n",
__func__, rt_structures[index].len, size);
}
#endif
offset += (size / 4); /* Next TLV record */
}
return 0;
}
/**
* Get the version. It is a structure, but a special one, so it is not using
* the generic function
*/
int rt_version_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
uint32_t *dout = pout;
hout->msg_id = RT_ACTION_SEND_VERSION;
hout->len = sizeof(struct wrnc_rt_version) / 4;
memcpy(dout, &version, sizeof(struct wrnc_rt_version));
return 0;
}
/**
* This is a generic setter that an external system can invoke
* to set a set of variable values.
* We use directly pointers and not an index
*/
int rt_variable_setter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
struct rt_variable *var;
uint32_t *din = pin, *mem, val;
int i;
/* we always have a pair of values */
if (hin->len % 2)
rt_send_nack(hin, pin, hout, pout);
/* Write all values in the proper place */
for (i = 0; i < hin->len; i += 2) {
if (din[i] >= rt_variables_count)
continue;
var = &rt_variables[din[i]];
mem = (uint32_t *) var->addr;
val = ((din[i + 1] & var->mask) << var->offset);
if (var->flags & RT_VARIABLE_FLAG_WO)
*mem = val;
else
*mem = (*mem & ~var->mask) | val;
#ifdef LIBRT_DEBUG
pp_printf("%s index %d/%d | [0x%x] = 0x%08x <- 0x%08x (0x%08x) | index in msg (%d/%d)\n",
__func__,
din[i], rt_variables_count,
mem, *mem, val, din[i + 1],
i + 1, hin->len - 1);
delay(100000);
#endif
}
/* Return back new values. Host can compare with what it sent
to spot errors */
if (hin->flags & WRNC_PROTO_FLAG_SYNC)
return rt_variable_getter(hin, pin, hout, pout);
return 0;
}
/**
* This is a generic getter that an external system can invoke
* to get a set of variable values
*/
int rt_variable_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
struct rt_variable *var;
uint32_t *dout = pout, *din = pin, *mem, val;
int i;
if (!hout || !pout)
return -1;
/* we always have a pair of values */
if (hin->len % 2)
return -1;
hout->msg_id = RT_ACTION_SEND_FIELD_GET;
/* Write all values in the proper place */
for (i = 0; i < hout->len; i += 2) {
if (din[i] >= rt_variables_count) {
dout[i] = ~0; /* Report invalid index */
continue;
}
dout[i] = din[i];
var = &rt_variables[dout[i]];
mem = (uint32_t *) var->addr;
val = (*mem >> var->offset) & var->mask;
dout[i + 1] = val;
#ifdef LIBRT_DEBUG
pp_printf("%s index %d/%d | [0x%x] = 0x%08x -> 0x%08x | index in msg (%d/%d)\n",
__func__,
dout[i], rt_variables_count,
mem, *mem, dout[i + 1],
i + 1, hin->len - 1);
delay(100000);
#endif
}
return 0;
}
/**
* This is a default action that answer on ping messages
*/
int rt_recv_ping(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
rt_send_ack(hin, pin, hout, pout);
return 0;
}
/**
* It runs the action associated with the given identifier
* @param[in] id action identifier
* @param[in] msg input message for the action
* @return 0 on success. A negative value on error
*/
static inline int rt_action_run(struct wrnc_proto_header *hin, void *pin)
{
action_t *action;
struct wrnc_msg out_buf;
struct wrnc_proto_header hout;
void *pout;
int err = 0;
if (hin->msg_id >= rt_action_count || !rt_actions[hin->msg_id]) {
pp_printf("Cannot dispatch ID 0x%x\n", hin->msg_id);
return -EINVAL;
}
action = rt_actions[hin->msg_id];
if (!(hin->flags & WRNC_PROTO_FLAG_SYNC)) {
/* Asynchronous message, then no output */
pp_printf("%s:%d\n", __func__, __LINE__);
return action(hin, pin, NULL, NULL);
}
#ifdef LIBRT_DEBUG
pp_printf("Message Input\n");
rt_print_header(hin);
rt_print_data(pin, 8);
#endif
/* Synchronous message */
out_buf = rt_mq_claim_out(hin);
pout = rt_proto_payload_get((void *) out_buf.data);
memcpy(&hout, hin, sizeof(struct wrnc_proto_header));
err = action(hin, pin, &hout, pout);
if (err)
rt_send_nack(hin, pin, &hout, NULL);
rt_proto_header_set((void *) out_buf.data, &hout);
#ifdef LIBRT_DEBUG
pp_printf("Message Output\n");
rt_print_header(&hout);
rt_print_data(pout, 8);
#endif
mq_msg_send(&hout);
return err;
}
/**
* It dispatch messages coming from a given HMQ slot
* @param[in] mq_in_slot message queue to poll for incoming messages
* @param[in] is_remote 1 if we are usin a remote queue
* @todo provide support for remote queue
*/
int rt_mq_action_dispatch(unsigned int mq_in_slot, unsigned int is_remote)
{
#ifdef RTPERFORMANCE
uint32_t sec, cyc, sec_n, cyc_n;
#endif
struct wrnc_proto_header *header;
struct wrnc_msg in_buf;
uint32_t p;
struct rt_action *action;
void *pin;
int err = 0;
/* HMQ control slot empty? */
p = mq_poll();
if (!(p & ( 1 << mq_in_slot)))
return -EAGAIN;
/* Get the message from the HMQ by claiming it */
in_buf = hmq_msg_claim_in(mq_in_slot, 8);
#ifdef LIBRT_DEBUG
pp_printf("Incoming message\n");
rt_print_data(in_buf.data, 8);
#endif
header = rt_proto_header_get((void *) in_buf.data);
pin = rt_proto_payload_get((void *) in_buf.data);
if (header->rt_app_id && header->rt_app_id != version.rt_id) {
pp_printf("Not for this application 0x%x\n", header->rt_app_id);
err = -EINVAL;
goto out;
}
#ifdef RTPERFORMANCE
rt_get_time(&sec, &cyc);
#endif
/* Run the correspondent action */
err = rt_action_run(header, pin);
if (err)
pp_printf("%s: action failure err: %d\n", __func__, err);
#ifdef RTPERFORMANCE
rt_get_time(&sec_n, &cyc_n);
pp_printf("%s: time %d", __func__, (cyc_n - cyc) * 8);
#endif
out:
mq_discard(0, mq_in_slot);
return err;
}
/**
* It get the current time from the internal WRNC timer
* @param[out] seconds
* @param[out] cycles
*/
void rt_get_time(uint32_t *seconds, uint32_t *cycles)
{
*seconds = lr_readl(WRN_CPU_LR_REG_TAI_SEC);
*cycles = lr_readl(WRN_CPU_LR_REG_TAI_CYCLES);
}
/*
* Copyright (C) 2015 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 3
*/
#ifndef __LIBRT_H__
#define __LIBRT_H__
#include <stdint.h>
#include "wrnc-common.h"
#include "hw/wrn_cpu_lr.h"
#include "rt-common.h"
#include "rt-mqueue.h"
#include "rt-message.h"
#include "pp-printf.h"
#define RT_VARIABLE_FLAG_REG (1 << 0)
#ifdef LIBRT_DEBUG
static inline void rt_print_data(uint32_t *d, unsigned int count)
{
int i;
for (i = 0; i < count; i++) {
pp_printf("%s: data[%d] = 0x%x\n", __func__, i , d[i]);
delay(1000);
}
}
static inline void rt_print_header(struct wrnc_proto_header *h)
{
pp_printf("header ----\n");
delay(1000);
pp_printf(" app_id 0x%x | msg_id %d | slot_io 0x%x | seq %d\n",
h->rt_app_id, h->msg_id, h->slot_io, h->seq);
delay(1000);
pp_printf(" len %d | flags 0x%x | trans 0x%x | time %d\n",
h->len, h->flags, h->trans, h->time);
delay(1000);
}
#endif
#define RT_VARIABLE_FLAG_WO (1 << 0)
/**
* Description of a RealTime variable that we want to export to user-space
*/
struct rt_variable {
uint32_t addr; /**< variable location (RAM, SMEM, CPU, peripherals)*/
uint32_t mask; /**< variable mask without offset applied */
uint8_t offset; /**< variable offset within the word */
uint32_t flags; /**< variable options */
};
typedef int (action_t)(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
/**
* Description of a RealTime structure that we want to export to user-space
*/
struct rt_structure {
void *struct_ptr; /**< structure location */
uint32_t len; /**< data structure lenght */
/* Maybe other option later in time */
};
extern int rt_mq_action_register(uint32_t id, action_t action);
extern int rt_mq_action_dispatch(unsigned int in_slot, unsigned int is_remote);
/**
* This is a helper that send back a simple ACK message. Keep it static inline
* to avoid a function call. We gain performance, we loose memory
*/
static inline void rt_send_ack(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
hout->msg_id = RT_ACTION_SEND_ACK;
hout->len = 0;
}
/**
* This is a helper that send back a simple NACK message. Keep it static inline
* to avoid a function call. We gain performance, we loose memory
*/
static inline void rt_send_nack(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout)
{
hout->msg_id = RT_ACTION_SEND_NACK;
hout->len = 0;
}
extern void rt_get_time(uint32_t *seconds, uint32_t *cycles);
extern void rt_action_export(action_t **actions, unsigned int count);
extern void rt_variable_export(struct rt_variable *variable,
unsigned int count);
extern int rt_variable_setter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
extern int rt_variable_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
extern void rt_structure_export(struct rt_structure *structures,
unsigned int count);
extern int rt_structure_setter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
extern int rt_structure_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
extern int rt_recv_ping(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
extern int rt_version_getter(struct wrnc_proto_header *hin, void *pin,
struct wrnc_proto_header *hout, void *pout);
#endif /* __LIBRT_H__ */
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#ifndef __RT_MESSAGE_H #ifndef __RT_MESSAGE_H
#define __RT_MESSAGE_H #define __RT_MESSAGE_H
#include <wrnc-common.h>
#ifdef WRNODE_RT #ifdef WRNODE_RT
enum wrnc_msg_direction { enum wrnc_msg_direction {
...@@ -42,15 +44,17 @@ struct wrnc_msg { ...@@ -42,15 +44,17 @@ struct wrnc_msg {
* It claims an output slot. This means that you get exclusive access to * It claims an output slot. This means that you get exclusive access to
* the slot. * the slot.
*/ */
static inline struct wrnc_msg hmq_msg_claim_out(int slot, int max_size) static inline struct wrnc_msg rt_mq_claim_out(struct wrnc_proto_header *h)
{ {
struct wrnc_msg b; struct wrnc_msg b;
int remote = !!(h->flags & WRNC_PROTO_FLAG_REMOTE);
int slot = h->slot_io & 0xF;
mq_claim(0, slot); mq_claim(remote, slot);
b.data = mq_map_out_buffer(0, slot); b.data = mq_map_out_buffer(remote, slot);
b.direction = WRNC_MSG_DIR_SEND; b.direction = WRNC_MSG_DIR_SEND;
b.max_size = max_size; b.max_size = h->len;
b.offset = 0; b.offset = 0;
b.datalen = 0; b.datalen = 0;
b.slot = slot; b.slot = slot;
...@@ -58,25 +62,63 @@ static inline struct wrnc_msg hmq_msg_claim_out(int slot, int max_size) ...@@ -58,25 +62,63 @@ static inline struct wrnc_msg hmq_msg_claim_out(int slot, int max_size)
return b; return b;
} }
/**
* Obsolete. Use rt_mq_claim_out
*/
static inline struct wrnc_msg hmq_msg_claim_out(int slot, int max_size)
{
struct wrnc_proto_header h = {
.slot_io = (slot & 0xF),
.len = max_size,
};
return rt_mq_claim_out(&h);
}
/** /**
* It claims an input slot. This mean that you get exclusive access to * It claims an input slot. This mean that you get exclusive access to
* the slot * the slot
*/ */
static inline struct wrnc_msg hmq_msg_claim_in(int slot, int max_size) static inline struct wrnc_msg rt_mq_claim_in(struct wrnc_proto_header *h)
{ {
struct wrnc_msg b; struct wrnc_msg b;
int slot = (h->slot_io >> 4) & 0xF;
b.data = mq_map_in_buffer(0, slot); b.data = mq_map_in_buffer(0, slot);
b.direction = WRNC_MSG_DIR_RECEIVE; b.direction = WRNC_MSG_DIR_RECEIVE;
b.max_size = max_size; b.max_size = h->len;
b.datalen = max_size; b.datalen = h->len;
b.offset = 0; b.offset = 0;
b.slot = slot; b.slot = slot;
return b; return b;
} }
/**
* Obsolete. Use rt_mq_claim_in
*/
static inline struct wrnc_msg hmq_msg_claim_in(int slot, int max_size)
{
struct wrnc_proto_header h = {
.slot_io = (slot & 0xF) << 4,
.len = max_size,
};
return rt_mq_claim_in(&h);
}
/**
* It send the message associate to the given header
* @param[in] h header of the message to be sent
*/
static inline void mq_msg_send(struct wrnc_proto_header *h)
{
int remote = !!(h->flags & WRNC_PROTO_FLAG_REMOTE);
mq_send(remote, (h->slot_io & 0xF),
h->len + (sizeof(struct wrnc_proto_header) / 4));
}
/** /**
* It enqueue the message in the slot, and it will be sent as soon as possible * It enqueue the message in the slot, and it will be sent as soon as possible
......
...@@ -8,7 +8,7 @@ WRNC ?= ../../../ ...@@ -8,7 +8,7 @@ WRNC ?= ../../../
LIB = libdemo.a LIB = libdemo.a
LOBJ := libdemo.o LOBJ := libdemo.o
CFLAGS += -Wall -ggdb -O0 -I. -I../include -I$(WRNC)/include -I$(WRNC)/lib $(EXTRACFLAGS) CFLAGS += -Wall -ggdb -O0 -I. -I../include -I$(WRNC)/include -I$(WRNC)/lib -I$(WRNC)/kernel $(EXTRACFLAGS)
LDLIBS += -L. -ldemo LDLIBS += -L. -ldemo
......
...@@ -9,6 +9,7 @@ WRNC ?= ../../../ ...@@ -9,6 +9,7 @@ WRNC ?= ../../../
CFLAGS += -Wall -ggdb -I. -I../include -I$(WRNC)/include -I$(WRNC)/lib -I../lib CFLAGS += -Wall -ggdb -I. -I../include -I$(WRNC)/include -I$(WRNC)/lib -I../lib
CFLAGS += $(EXTRACFLAGS) CFLAGS += $(EXTRACFLAGS)
CFLAGS += $(EXTRACFLAGS)
LDLIBS += -L../lib -lwrtd -L$(WRNC)/lib -lwrnc LDLIBS += -L../lib -lwrtd -L$(WRNC)/lib -lwrnc
PROGS := wrtd-boot PROGS := wrtd-boot
......
/*
* Copyright (C) 2015 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 3
*/
#ifndef __WRNC_COMMON_H__
#define __WRNC_COMMON_H__
#include <string.h>
/**
* It describes the version running on the embedded CPU
*/
struct wrnc_rt_version {
uint32_t fpga_id; /**< FPGA identifier expected to run the RT app */
uint32_t rt_id; /**< RT application identifier */
uint32_t rt_version; /**< RT application version*/
uint32_t git_version; /**< git commit SHA1 of the compilation time */
};
enum rt_action_recv_standard {
RT_ACTION_RECV_PING = 0,
RT_ACTION_RECV_FIELD_SET,
RT_ACTION_RECV_FIELD_GET,
RT_ACTION_RECV_STRUCT_SET,
RT_ACTION_RECV_STRUCT_GET,
RT_ACTION_RECV_VERSION,
RT_ACTION_SEND_ACK,
RT_ACTION_SEND_NACK,
RT_ACTION_SEND_FIELD_GET,
RT_ACTION_SEND_STRUCT_GET,
RT_ACTION_SEND_VERSION,
__RT_ACTION_RECV_STANDARD_NUMBER,
};
/**< __MAX_ACTION_RECV coming from GCC on compilation */
#define MAX_ACTION_RECV (__MAX_ACTION_RECV + __RT_ACTION_RECV_STANDARD_NUMBER)
/**< __MAX_ACTION_SEND coming from GCC on compilation */
#define MAX_ACTION_SEND (__MAX_ACTION_SEND + __RT_ACTION_SEND_STANDARD_NUMBER)
/* Protocol Definition */
#define WRNC_PROTO_FLAG_REMOTE (1 << 0)
#define WRNC_PROTO_FLAG_SYNC (1 << 1)
#define WRNC_PROTO_FLAG_RPC (1 << 2)
#define WRNC_PROTO_FLAG_PERIODICAL (1 << 3)
/**
* Protocol header definition
*/
struct wrnc_proto_header {
uint16_t rt_app_id; /**< Real-Time application unique identifier */
uint8_t msg_id; /**< Message identifier */
uint8_t slot_io; /**< Message Queue IO to use */
uint32_t seq; /**< sequence number */
uint8_t len; /**< message data lenght */
uint8_t flags; /**< protocol flags */
uint8_t __unused; /**< not used, future use */
uint8_t trans; /**< transaction descriptor - flag and seq number */
uint32_t time;
};
/**
* It extracts the header from a raw message
* @param[in] raw_msg raw message
* @param[out] header the header from the message
*/
static inline struct wrnc_proto_header *rt_proto_header_get(void *raw_msg)
{
return raw_msg;
}
/**
* It embeds the header from a raw message
* @param[in] raw_msg raw message
* @param[out] header the header from the message
*/
static inline void rt_proto_header_set(void *raw_msg,
struct wrnc_proto_header *header)
{
memcpy(raw_msg, header, sizeof(struct wrnc_proto_header));
}
/**
* It returns the pointer where it starts the message payload
* @param[in] raw_msg raw message
* @param[out] header the header from the message
*/
static inline void *rt_proto_payload_get(void *raw_msg)
{
return (raw_msg + sizeof(struct wrnc_proto_header));
}
#endif
...@@ -3,8 +3,11 @@ ...@@ -3,8 +3,11 @@
# build a particular environment. # build a particular environment.
-include Makefile.specific -include Makefile.specific
WRNC ?= ../
LIB = libwrnc.a LIB = libwrnc.a
LOBJ := libwrnc.o LOBJ := libwrnc.o
LOBJ += libwrnc-rt-msg.o
CFLAGS += -Wall -ggdb -I. -I$(WRNC)/include $(EXTRACFLAGS) CFLAGS += -Wall -ggdb -I. -I$(WRNC)/include $(EXTRACFLAGS)
LDFLAGS = -L. -lwrnc LDFLAGS = -L. -lwrnc
......
/*
* Copyright (C) 2015 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*
* Released according to the GNU GPL, version 3
*/
#include <libwrnc.h>
#include <errno.h>
/**
* Retrieve the current Real-Time Application version running. This is a
* synchronous message.
* @param[in] wrnc device token
* @param[out] version FPGA, RT and GIT version
* @param[in] hmq_in hmq slot index where send the message
* @param[in] hmq_out hmq slot index where you expect the answer
* @return 0 on success, -1 on error and errno is set appropriately
*/
int wrnc_rt_version_get(struct wrnc_dev *wrnc, struct wrnc_rt_version *version,
unsigned int hmq_in, unsigned int hmq_out)
{
struct wrnc_proto_header hdr;
struct wrnc_hmq *hmq;
struct wrnc_msg msg;
int err;
memset(&msg, 0, sizeof(struct wrnc_msg));
memset(&hdr, 0, sizeof(struct wrnc_proto_header));
hdr.msg_id = RT_ACTION_RECV_VERSION;
hdr.slot_io = (hmq_in << 4) | hmq_out;
hdr.flags = WRNC_PROTO_FLAG_SYNC;
hdr.len = 0;
wrnc_message_pack(&msg, &hdr, NULL);
hmq = wrnc_hmq_open(wrnc, hmq_in, WRNC_HMQ_INCOMING);
if (!hmq)
return -1;
/* Send the message and get answer */
err = wrnc_hmq_send_and_receive_sync(hmq, hmq_out, &msg,
wrnc_default_timeout_ms);
wrnc_hmq_close(hmq);
wrnc_message_unpack(&msg, &hdr, version);
if (hdr.msg_id != RT_ACTION_SEND_VERSION) {
errno = EWRNC_INVALID_MESSAGE;
return -1;
}
return 0;
}
/**
* It checks if a Real-Time application is running and answering to messages
* @param[in] wrnc device token
* @param[in] hmq_in hmq slot index where send the message
* @param[in] hmq_out hmq slot index where you expect the answer
* @return 0 on success, -1 on error and errno is set appropriately
*/
int wrnc_rt_ping(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out)
{
struct wrnc_proto_header hdr;
struct wrnc_hmq *hmq;
struct wrnc_msg msg;
int err;
memset(&hdr, 0, sizeof(struct wrnc_proto_header));
hdr.msg_id = RT_ACTION_RECV_PING;
hdr.slot_io = (hmq_in << 4) | hmq_out;
hdr.flags = WRNC_PROTO_FLAG_SYNC;
hdr.len = 0;
wrnc_message_pack(&msg, &hdr, NULL);
hmq = wrnc_hmq_open(wrnc, hmq_in, WRNC_HMQ_INCOMING);
if (!hmq)
return -1;
/* Send the message and get answer */
err = wrnc_hmq_send_and_receive_sync(hmq, hmq_out, &msg,
wrnc_default_timeout_ms);
wrnc_hmq_close(hmq);
if (hdr.msg_id != RT_ACTION_SEND_ACK) {
errno = EWRNC_INVALID_MESSAGE;
return -1;
}
return 0;
}
/**
* Real implementation to read/write variables
*/
static inline int wrnc_rt_variable(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint8_t msg_id_in, uint8_t msg_id_out,
uint32_t *variables,
unsigned int n_variables,
unsigned int sync)
{
struct wrnc_proto_header hdr;
struct wrnc_msg msg;
struct wrnc_hmq *hmq;
int err;
hmq = wrnc_hmq_open(wrnc, hmq_in, WRNC_HMQ_INCOMING);
if (!hmq)
return -1;
memset(&hdr, 0, sizeof(struct wrnc_proto_header));
hdr.msg_id = msg_id_in;
hdr.slot_io = (hmq->index << 4) | (hmq_out & 0xF);
hdr.len = n_variables * 2;
hdr.flags = sync ? WRNC_PROTO_FLAG_SYNC : 0;
/* Send asynchronous message, we do not wait for answers */
wrnc_message_pack(&msg, &hdr, variables);
if (hdr.flags & WRNC_PROTO_FLAG_SYNC) {
err = wrnc_hmq_send_and_receive_sync(hmq, hmq_out, &msg, 1000);
wrnc_message_unpack(&msg, &hdr, variables);
/* Check if it is the answer that we are expecting */
if (!err && hdr.msg_id != msg_id_out)
err = -1;
} else {
err = wrnc_hmq_send(hmq, &msg);
}
wrnc_hmq_close(hmq);
return err;
}
/**
* It sends/receive a set of variables to/from the Real-Time application.
*
* The 'variables' field data format is the following
*
* 0 1 2 3 4 5 ...
* +-----+-----+-----+-----+-----+-----+
* | IDX | VAL | IDX | VAL | IDX | VAL | ...
* +-----+-----+-----+-----+-----+-----+
*
* IDX is the variable index defined by the real-time application
* VAL is the associated value
*
* By setting the flag 'sync' you will send a synchronous message, otherwise
* it is asyncrhonous. When synchronous the 'variables' field will be
* overwritten by the syncrhonous answer; the answer contains the read back
* values for the requested variable after the set operation. You can use
* this to verify. You can use synchrounous messages to verify that you
* variable are properly set.
* @param[in] wrnc device token
* @param[in] hmq_in input slot index [0, 15]
* @param[in] hmq_out output slot index [0, 15]
* @param[in|out] variables
* @param[in] n_variables number of variables to set. In other words,
* the number of indexes you have in the 'variables' fields.
* @param[in] sync set if you want a synchronous message
*/
int wrnc_rt_variable_set(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint32_t *variables,
unsigned int n_variables,
unsigned int sync)
{
return wrnc_rt_variable(wrnc, hmq_in, hmq_out,
RT_ACTION_RECV_FIELD_SET,
RT_ACTION_SEND_FIELD_GET,
variables, n_variables, sync);
}
/**
* It receive a set of variables from the Real-Time application.
*
* The 'variables' field data format is the following
*
* 0 1 2 3 4 5 ...
* +-----+-----+-----+-----+-----+-----+
* | IDX | VAL | IDX | VAL | IDX | VAL | ...
* +-----+-----+-----+-----+-----+-----+
*
* IDX is the variable index defined by the real-time application
* VAL is the associated value
*
* This kind of message is always synchronous. The 'variables' field will be
* overwritten by the syncrhonous answer; the answer contains the read back
* values for the requested variables.
* @param[in] wrnc device token
* @param[in] hmq_in input slot index [0, 15]
* @param[in] hmq_out output slot index [0, 15]
* @param[in|out] variables
* @param[in] n_variables number of variables to set. In other words,
* the number of indexes you have in the 'variables' fields
*/
int wrnc_rt_variable_get(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint32_t *variables,
unsigned int n_variables)
{
return wrnc_rt_variable(wrnc, hmq_in, hmq_out,
RT_ACTION_RECV_FIELD_GET,
RT_ACTION_SEND_FIELD_GET,
variables, n_variables, 1);
}
/**
* Real implementation to read/write structures
*/
static int wrnc_rt_structure(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint8_t msg_id_in, uint8_t msg_id_out,
struct wrnc_structure_tlv *tlv,
unsigned int n_tlv,
unsigned int sync)
{
struct wrnc_proto_header hdr;
struct wrnc_msg msg;
struct wrnc_hmq *hmq;
int err, i;
hmq = wrnc_hmq_open(wrnc, hmq_in, WRNC_HMQ_INCOMING);
if (!hmq)
return -1;
memset(&hdr, 0, sizeof(struct wrnc_proto_header));
hdr.msg_id = msg_id_in;
hdr.slot_io = (hmq->index << 4) | (hmq_out & 0xF);
hdr.flags = sync ? WRNC_PROTO_FLAG_SYNC : 0;
/* Send asynchronous message, we do not wait for answers */
for (i = 0; i < n_tlv; ++i)
wrnc_message_structure_push(&msg, &hdr, &tlv[i]);
if (hdr.flags & WRNC_PROTO_FLAG_SYNC) {
err = wrnc_hmq_send_and_receive_sync(hmq, hmq_out, &msg, 1000);
for (i = 0; i < n_tlv; ++i)
wrnc_message_structure_pop(&msg, &hdr, &tlv[i]);
/* Check if it is the answer that we are expecting */
if (!err && hdr.msg_id != msg_id_out)
err = -1;
} else {
err = wrnc_hmq_send(hmq, &msg);
}
wrnc_hmq_close(hmq);
return err;
}
/**
* It sends/receives a set of structures within TLV records
* @param[in] wrnc device token
* @param[in] hmq_in input slot index [0, 15]
* @param[in] hmq_out output slot index [0, 15]
* @param[in|out] structures
*/
int wrnc_rt_structure_set(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
struct wrnc_structure_tlv *tlv, unsigned int n_tlv,
unsigned int sync)
{
return wrnc_rt_structure(wrnc, hmq_in, hmq_out,
RT_ACTION_RECV_STRUCT_SET,
RT_ACTION_SEND_STRUCT_GET,
tlv, n_tlv, sync);
}
/**
* It receives a set of structures within TLV records
* @param[in] wrnc device token
* @param[in] hmq_in input slot index [0, 15]
* @param[in] hmq_out output slot index [0, 15]
* @param[in|out] structures
*/
int wrnc_rt_structure_get(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
struct wrnc_structure_tlv *tlv, unsigned int n_tlv)
{
return wrnc_rt_structure(wrnc, hmq_in, hmq_out,
RT_ACTION_RECV_STRUCT_GET,
RT_ACTION_SEND_STRUCT_GET,
tlv, n_tlv, 1);
}
...@@ -20,11 +20,14 @@ ...@@ -20,11 +20,14 @@
#include "libwrnc-internal.h" #include "libwrnc-internal.h"
const unsigned int wrnc_default_timeout_ms = 1000;
static char *wrnc_error_str[] = { static char *wrnc_error_str[] = {
"Cannot parse data from sysfs attribute", "Cannot parse data from sysfs attribute",
"Invalid slot", "Invalid slot",
"Operation not yet implemented", "Operation not yet implemented",
"The HMQ slot is close", "The HMQ slot is close",
"Invalid message",
NULL, NULL,
}; };
......
...@@ -13,11 +13,16 @@ ...@@ -13,11 +13,16 @@
extern "C" { extern "C" {
#endif #endif
#include <string.h>
#include <endian.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <poll.h> #include <poll.h>
#include "wrnc-common.h"
#include "wrnc-user.h" #include "wrnc-user.h"
extern const unsigned int wrnc_default_timeout_ms;
struct wrnc_dev; struct wrnc_dev;
#define WRNC_SYSFS_PATH_LEN 128 #define WRNC_SYSFS_PATH_LEN 128
...@@ -71,6 +76,7 @@ enum wrnc_error_number { ...@@ -71,6 +76,7 @@ enum wrnc_error_number {
EWRNC_INVAL_SLOT, /**< invalid slot */ EWRNC_INVAL_SLOT, /**< invalid slot */
EWRNC_NO_IMPLEMENTATION, /**< a prototype is not implemented */ EWRNC_NO_IMPLEMENTATION, /**< a prototype is not implemented */
EWRNC_HMQ_CLOSE, /**< The HMQ is closed */ EWRNC_HMQ_CLOSE, /**< The HMQ is closed */
EWRNC_INVALID_MESSAGE, /**< Invalid message */
__EWRNC_MAX, __EWRNC_MAX,
}; };
...@@ -181,6 +187,183 @@ extern int wrnc_debug_message_get(struct wrnc_dbg *dbg, ...@@ -181,6 +187,183 @@ extern int wrnc_debug_message_get(struct wrnc_dbg *dbg,
char *buf, size_t count); char *buf, size_t count);
/**@}*/ /**@}*/
/**
* @defgroup rtmsg Real Time service messages
* Message builders for RT service messages
* @{
*/
extern int wrnc_rt_version_get(struct wrnc_dev *wrnc,
struct wrnc_rt_version *version,
unsigned int hmq_in, unsigned int hmq_out);
extern int wrnc_rt_ping(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out);
/**@}*/
/**
* It embeds the header into the message
* @param[in] msg the wrnc message
* @param[in] hdr the header you want to embed into the message
*/
static inline void wrnc_message_header_set(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr)
{
uint32_t *hdr32 = (uint32_t *) hdr;
msg->data[0] = htobe32((hdr32[0] & 0xFFFF0000) |
((hdr32[0] >> 8) & 0x00FF) |
((hdr32[0] << 8) & 0xFF00));
msg->data[1] = hdr32[1];
msg->data[2] = htobe32(hdr32[2]);
msg->data[3] = hdr32[3];
msg->datalen = sizeof(struct wrnc_proto_header) / 4;
msg->max_size = msg->datalen;
}
/**
* It retrieves the header from the message
* @param[in] msg the wrnc message
* @param[out] hdr the header from the message
*/
static inline void wrnc_message_header_get(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr)
{
uint32_t *hdr32 = (uint32_t *) hdr;
hdr32[0] = be32toh((msg->data[0] & 0x0000FFFF) |
((msg->data[0] >> 8) & 0x00FF0000) |
((msg->data[0] << 8) & 0xFF000000));
hdr32[1] = msg->data[1];
hdr32[2] = be32toh(msg->data[2]);
hdr32[3] = msg->data[3];
}
/**
* It packs a message to send to the HMQ. The function uses the header to get
* the payload size. Rembember that the payload length unit is the 32bit word.
* Remind also that the WRNC VHDL code, will convert a given 32bit word between
* little endian and big endian
* @param[out] msg raw message
* @param[in] hdr message header
* @param[in] payload data
*/
static inline void wrnc_message_pack(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr,
void *payload)
{
void *data = msg->data;
wrnc_message_header_set(msg, hdr);
if (!payload)
return;
memcpy(data + sizeof(struct wrnc_proto_header), payload,
hdr->len * 4);
msg->datalen += hdr->len;
msg->max_size = msg->datalen;
}
/**
* it unpacks a message coming from the HMQ by separating the header from
* the payload. You will find the payload size in the header.
* Rembember that the payload length unit is the 32bit word.
* Remind also that the WRNC VHDL code, will convert a given 32bit word between
* little endian and big endian
* @param[in] msg raw message
* @param[out] hdr message header
* @param[out] payload data
*/
static inline void wrnc_message_unpack(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr,
void *payload)
{
void *data = msg->data;
wrnc_message_header_get(msg, hdr);
if (!payload)
return;
memcpy(payload, data + sizeof(struct wrnc_proto_header),
hdr->len * 4);
}
struct wrnc_structure_tlv {
uint32_t index;
void *structure;
size_t size;
};
static inline void wrnc_message_structure_push(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr,
struct wrnc_structure_tlv *tlv)
{
unsigned int offset = sizeof(struct wrnc_proto_header) / 4 + hdr->len;
void *data;
msg->data[offset++] = tlv->index;
msg->data[offset++] = tlv->size;
data = &msg->data[offset];
memcpy(data, tlv->structure, tlv->size);
hdr->len += 2 + (tlv->size / 4);
wrnc_message_header_set(msg, hdr);
msg->datalen = hdr->len + sizeof(struct wrnc_proto_header) / 4;
}
/**
* A TLV record containing a structure will be take from the message head.
* The function will update the message lenght in the header by removing
* the size occupied by the last record.
* @param[in] msg raw message
* @param[in] hdr message header
* @param[out] tlv TLV record containing a structure
*/
static inline void wrnc_message_structure_pop(struct wrnc_msg *msg,
struct wrnc_proto_header *hdr,
struct wrnc_structure_tlv *tlv)
{
unsigned int offset = sizeof(struct wrnc_proto_header) / 4;
void *data;
wrnc_message_header_get(msg, hdr);
if (hdr->len < 3)
return; /* there is nothing to read */
tlv->index = msg->data[offset++];
tlv->size = msg->data[offset++];
if (tlv->size / 4 > hdr->len - 2)
return; /* TLV greater than what is declared in header */
data = &msg->data[offset];
memcpy(tlv->structure, data, tlv->size);
hdr->len = hdr->len - (tlv->size / 4) - 2; /* -2 because of index
and size in TLV */
/* shift all data - +8 because of index and size which are uint32_t */
memcpy(data, data + tlv->size + 8, hdr->len * 4);
}
extern int wrnc_rt_variable_set(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint32_t *variables,
unsigned int n_variables,
unsigned int sync);
extern int wrnc_rt_variable_get(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
uint32_t *variables,
unsigned int n_variables);
extern int wrnc_rt_structure_set(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
struct wrnc_structure_tlv *tlv,
unsigned int n_tlv,
unsigned int sync);
extern int wrnc_rt_structure_get(struct wrnc_dev *wrnc,
unsigned int hmq_in, unsigned int hmq_out,
struct wrnc_structure_tlv *tlv,
unsigned int n_tlv);
#ifdef __cplusplus #ifdef __cplusplus
}; };
#endif #endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment