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
OBJS += $(WRNC)/applications/common/rt/vsprintf-xint.o
OBJS += $(WRNC)/applications/common/rt/printf.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
......
/*
* 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 @@
#ifndef __RT_MESSAGE_H
#define __RT_MESSAGE_H
#include <wrnc-common.h>
#ifdef WRNODE_RT
enum wrnc_msg_direction {
......@@ -42,15 +44,17 @@ struct wrnc_msg {
* It claims an output slot. This means that you get exclusive access to
* 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;
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.max_size = max_size;
b.max_size = h->len;
b.offset = 0;
b.datalen = 0;
b.slot = slot;
......@@ -58,25 +62,63 @@ static inline struct wrnc_msg hmq_msg_claim_out(int slot, int max_size)
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
* 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;
int slot = (h->slot_io >> 4) & 0xF;
b.data = mq_map_in_buffer(0, slot);
b.direction = WRNC_MSG_DIR_RECEIVE;
b.max_size = max_size;
b.datalen = max_size;
b.max_size = h->len;
b.datalen = h->len;
b.offset = 0;
b.slot = slot;
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
......
......@@ -8,7 +8,7 @@ WRNC ?= ../../../
LIB = libdemo.a
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
......
......@@ -9,6 +9,7 @@ WRNC ?= ../../../
CFLAGS += -Wall -ggdb -I. -I../include -I$(WRNC)/include -I$(WRNC)/lib -I../lib
CFLAGS += $(EXTRACFLAGS)
CFLAGS += $(EXTRACFLAGS)
LDLIBS += -L../lib -lwrtd -L$(WRNC)/lib -lwrnc
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 @@
# build a particular environment.
-include Makefile.specific
WRNC ?= ../
LIB = libwrnc.a
LOBJ := libwrnc.o
LOBJ += libwrnc-rt-msg.o
CFLAGS += -Wall -ggdb -I. -I$(WRNC)/include $(EXTRACFLAGS)
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 @@
#include "libwrnc-internal.h"
const unsigned int wrnc_default_timeout_ms = 1000;
static char *wrnc_error_str[] = {
"Cannot parse data from sysfs attribute",
"Invalid slot",
"Operation not yet implemented",
"The HMQ slot is close",
"Invalid message",
NULL,
};
......
......@@ -13,11 +13,16 @@
extern "C" {
#endif
#include <string.h>
#include <endian.h>
#include <stdint.h>
#include <stdio.h>
#include <poll.h>
#include "wrnc-common.h"
#include "wrnc-user.h"
extern const unsigned int wrnc_default_timeout_ms;
struct wrnc_dev;
#define WRNC_SYSFS_PATH_LEN 128
......@@ -71,6 +76,7 @@ enum wrnc_error_number {
EWRNC_INVAL_SLOT, /**< invalid slot */
EWRNC_NO_IMPLEMENTATION, /**< a prototype is not implemented */
EWRNC_HMQ_CLOSE, /**< The HMQ is closed */
EWRNC_INVALID_MESSAGE, /**< Invalid message */
__EWRNC_MAX,
};
......@@ -181,6 +187,183 @@ extern int wrnc_debug_message_get(struct wrnc_dbg *dbg,
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
};
#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