Commit b52c9487 authored by Jean-Claude BAU's avatar Jean-Claude BAU

HAL implementation for Low Phase Drift Calibration

parents 9da6b498 b17856a4
[submodule "userspace/mini-rpc"]
path = userspace/mini-rpc
url = git://github.com/rubini/mini-rpc.git
[submodule "userspace/ppsi"]
path = userspace/ppsi
url = ../ppsi.git
......@@ -10,15 +10,19 @@
*/
#ifndef __WBGEN2_REGDEFS_ENDPOINT-MDIO_WB
#define __WBGEN2_REGDEFS_ENDPOINT-MDIO_WB
#ifndef __WBGEN2_REGDEFS_ENDPOINT_MDIO_WB
#define __WBGEN2_REGDEFS_ENDPOINT_MDIO_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
......@@ -371,6 +375,33 @@
#define MDIO_WR_SPEC_BSLIDE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define MDIO_WR_SPEC_BSLIDE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for register: MDIO Extended Control Register */
/* definitions for field: Loopback - detailed in reg: MDIO Extended Control Register */
#define MDIO_ECTRL_LPBCK_VEC_MASK WBGEN2_GEN_MASK(0, 3)
#define MDIO_ECTRL_LPBCK_VEC_SHIFT 0
#define MDIO_ECTRL_LPBCK_VEC_W(value) WBGEN2_GEN_WRITE(value, 0, 3)
#define MDIO_ECTRL_LPBCK_VEC_R(reg) WBGEN2_GEN_READ(reg, 0, 3)
/* definitions for field: SFP TX Fault Status in reg: MDIO Extended Control Register */
#define MDIO_ECTRL_SFP_TX_FAULT WBGEN2_GEN_MASK(3, 1)
/* definitions for field: SFP LOS in reg: MDIO Extended Control Register */
#define MDIO_ECTRL_SFP_LOSS WBGEN2_GEN_MASK(4, 1)
/* definitions for field: SFP TX Disable in reg: MDIO Extended Control Register */
#define MDIO_ECTRL_SFP_TX_DISABLE WBGEN2_GEN_MASK(5, 1)
/* definitions for field: tx_prbs_sel in reg: MDIO Extended Control Register */
#define MDIO_ECTRL_TX_PRBS_SEL_MASK WBGEN2_GEN_MASK(8, 3)
#define MDIO_ECTRL_TX_PRBS_SEL_SHIFT 8
#define MDIO_ECTRL_TX_PRBS_SEL_W(value) WBGEN2_GEN_WRITE(value, 8, 3)
#define MDIO_ECTRL_TX_PRBS_SEL_R(reg) WBGEN2_GEN_READ(reg, 8, 3)
/* definitions for register: Low phase drift calibration status register */
/* definitions for register: Low phase drift calibration control register */
PACKED struct MDIO_WB {
/* [0x0]: REG MDIO Control Register */
uint32_t MCR;
......@@ -392,6 +423,12 @@ PACKED struct MDIO_WB {
uint32_t ESTATUS;
/* [0x40]: REG WhiteRabbit-specific Configuration Register */
uint32_t WR_SPEC;
/* [0x44]: REG MDIO Extended Control Register */
uint32_t ECTRL;
/* [0x48]: REG Low phase drift calibration status register */
uint32_t LPC_PHY_STAT;
/* [0x4c]: REG Low phase drift calibration control register */
uint32_t LPC_PHY_CTRL;
};
#endif
......@@ -36,6 +36,7 @@
peripheral {
name = "WR Endpoint 1000base-X TBI PCS register block";
description = "WR Endpoint 1000base-X TBI PCS register block";
hdl_entity = "ep_pcs_tbi_mdio_wb";
prefix = "MDIO";
......@@ -153,15 +154,15 @@ peripheral {
field {
name = "Loopback";
description = "1 = enable loopback mode \
0 = disable loopback mode \
With the TBI version, loopback bit is connected to PHY loopback enable pin. When set to 1, indicates to the external PHY to enter loopback mode";
description = "1 enable loopback mode \
0 = disable loopback mode \
With the TBI version, loopback bit is connected to PHY loopback enable pin. When set to 1, indicates to the external PHY to enter loopback mode";
prefix = "loopback";
align = 14;
align = 14;
type = BIT;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
field {
......@@ -720,5 +721,99 @@ peripheral {
clock = "rx_clk_i";
};
};
reg {
name = "MDIO Extended Control Register";
prefix = "ECTRL";
field {
name = "Loopback - detailed";
description = "Loopback vector for Xilinx PHYs: \
100 = far end loopback mode \
000 = normal mode \
See also Transceiver documentation (for example Xilinx UG476 Table 2-37 and Figure 2-23 \
LOOPBACK bit set to 1 in MCR register may override LPBCK_VEC depending on the PHY wrapper implementation";
prefix = "lpbck_vec";
align = 0;
size = 3;
value = 0;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
field {
name = "SFP TX Fault Status";
description = "1 = Some kind of laser failure\
0 = SFP Laser okay";
prefix = "sfp_tx_fault";
type = BIT;
access_bus=READ_ONLY;
access_dev=WRITE_ONLY;
};
field {
name = "SFP LOS";
description = "1 = Loss of signal\
0 = SFP Receiver signal strength okay";
prefix = "sfp_loss";
type = BIT;
access_bus=READ_ONLY;
access_dev=WRITE_ONLY;
};
field {
name = "SFP TX Disable";
description = "Disables the SFP Transmitter \
1 = SFP TX Disabled\
0 = SFP TX Enabled";
prefix = "sfp_tx_disable";
type = BIT;
value = 0;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
field {
name = "tx_prbs_sel";
description = "PRBS selection \
000 = Normal mode\
0010 = PRBS-7";
prefix = "tx_prbs_sel";
align = 8;
size = 3;
value = 0;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
reg {
name = "Low phase drift calibration status register";
description = "Low phase drift calibration status register - dependent on calibrated PHY";
prefix = "LPC_PHY_STAT";
field {
name = "Control word 0";
size = 16;
type = SLV;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
name = "Low phase drift calibration control register";
description = "Low phase drift calibration control register - dependent on calibrated PHY";
prefix = "LPC_PHY_CTRL";
field {
name = "Control word 0";
size = 16;
type = SLV;
access_bus = READ_WRITE;
access_dev = READ_ONLY;
};
};
};
......@@ -13,12 +13,16 @@
#ifndef __WBGEN2_REGDEFS_ENDPOINT
#define __WBGEN2_REGDEFS_ENDPOINT
#ifdef __KERNEL__
#include <linux/types.h>
#else
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
......@@ -52,6 +56,9 @@
/* definitions for field: Receive path enable in reg: Endpoint Control Register */
#define EP_ECR_RX_EN WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Generate preamble shrinkage in reg: Endpoint Control Register */
#define EP_ECR_TXSHRIN_EN WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Feature present: VLAN tagging in reg: Endpoint Control Register */
#define EP_ECR_FEAT_VLAN WBGEN2_GEN_MASK(24, 1)
......@@ -64,6 +71,9 @@
/* definitions for field: Feature present: DPI packet classifier in reg: Endpoint Control Register */
#define EP_ECR_FEAT_DPI WBGEN2_GEN_MASK(27, 1)
/* definitions for field: Feature present: low phase drift calibration in reg: Endpoint Control Register */
#define EP_ECR_FEAT_LPC WBGEN2_GEN_MASK(28, 1)
/* definitions for register: Timestamping Control Register */
/* definitions for field: Transmit timestamping enable in reg: Timestamping Control Register */
......@@ -84,7 +94,6 @@
/* definitions for field: RX timestamper calibration result flag in reg: Timestamping Control Register */
#define EP_TSCR_RX_CAL_RESULT WBGEN2_GEN_MASK(5, 1)
/* definitions for register: RX Deframer Control Register */
/* definitions for field: RX accept runts in reg: RX Deframer Control Register */
......@@ -189,12 +198,18 @@
/* definitions for register: Flow Control Register */
/* definitions for field: RX Pause enable in reg: Flow Control Register */
/* definitions for field: RX Pause 802.3 enable in reg: Flow Control Register */
#define EP_FCR_RXPAUSE WBGEN2_GEN_MASK(0, 1)
/* definitions for field: TX Pause enable in reg: Flow Control Register */
/* definitions for field: TX Pause 802.3 enable in reg: Flow Control Register */
#define EP_FCR_TXPAUSE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Rx Pause 802.1Q enable in reg: Flow Control Register */
#define EP_FCR_RXPAUSE_802_1Q WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Tx Pause 802.1Q enable (not implemented) in reg: Flow Control Register */
#define EP_FCR_TXPAUSE_802_1Q WBGEN2_GEN_MASK(3, 1)
/* definitions for field: TX pause threshold in reg: Flow Control Register */
#define EP_FCR_TX_THR_MASK WBGEN2_GEN_MASK(8, 8)
#define EP_FCR_TX_THR_SHIFT 8
......@@ -276,9 +291,35 @@
/* definitions for field: DMTD Phase shift value ready in reg: DMTD Status register */
#define EP_DMSR_PS_RDY WBGEN2_GEN_MASK(24, 1)
/* definitions for RAM: Event counters memory */
#define EP_RMON_RAM_BYTES 0x00000080 /* size in bytes */
#define EP_RMON_RAM_WORDS 0x00000020 /* size in 32-bit words, 32-bit aligned */
/* definitions for register: PCK Injection CTRL */
/* definitions for field: Config: Interframe GAP in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_CONF_IFG_MASK WBGEN2_GEN_MASK(0, 16)
#define EP_INJ_CTRL_PIC_CONF_IFG_SHIFT 0
#define EP_INJ_CTRL_PIC_CONF_IFG_W(value) WBGEN2_GEN_WRITE(value, 0, 16)
#define EP_INJ_CTRL_PIC_CONF_IFG_R(reg) WBGEN2_GEN_READ(reg, 0, 16)
/* definitions for field: Config: packet pattern sel id in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_CONF_SEL_MASK WBGEN2_GEN_MASK(16, 3)
#define EP_INJ_CTRL_PIC_CONF_SEL_SHIFT 16
#define EP_INJ_CTRL_PIC_CONF_SEL_W(value) WBGEN2_GEN_WRITE(value, 16, 3)
#define EP_INJ_CTRL_PIC_CONF_SEL_R(reg) WBGEN2_GEN_READ(reg, 16, 3)
/* definitions for field: Config: valid in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_CONF_VALID WBGEN2_GEN_MASK(19, 1)
/* definitions for field: Mode: packet generate mode in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_MODE_ID_MASK WBGEN2_GEN_MASK(20, 3)
#define EP_INJ_CTRL_PIC_MODE_ID_SHIFT 20
#define EP_INJ_CTRL_PIC_MODE_ID_W(value) WBGEN2_GEN_WRITE(value, 20, 3)
#define EP_INJ_CTRL_PIC_MODE_ID_R(reg) WBGEN2_GEN_READ(reg, 20, 3)
/* definitions for field: Mode: valid in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_MODE_VALID WBGEN2_GEN_MASK(23, 1)
/* definitions for field: Frame Generation Enabled in reg: PCK Injection CTRL */
#define EP_INJ_CTRL_PIC_ENA WBGEN2_GEN_MASK(24, 1)
PACKED struct EP_WB {
/* [0x0]: REG Endpoint Control Register */
......@@ -315,10 +356,8 @@ PACKED struct EP_WB {
uint32_t DMCR;
/* [0x40]: REG DMTD Status register */
uint32_t DMSR;
/* padding to: 32 words */
uint32_t __padding_0[15];
/* [0x80 - 0xff]: RAM Event counters memory, 32 32-bit words, 32-bit aligned, word-addressable */
uint32_t RMON_RAM [32];
/* [0x44]: REG PCK Injection CTRL */
uint32_t INJ_CTRL;
};
#endif
......@@ -86,6 +86,17 @@ peripheral {
type = BIT;
};
field {
name = "Generate preamble shrinkage";
prefix = "TXSHRIN_en";
description = "1: TX preamble shrinkage enabled\
0: TX preamble shrinkage disabled";
access_bus = READ_WRITE;
access_dev = READ_ONLY;
type = BIT;
};
field {
name = "Feature present: VLAN tagging";
description = "1: this implementation of WR Endpoint supports VLAN processing \
......@@ -130,6 +141,16 @@ peripheral {
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
field {
name = "Feature present: low phase drift calibration";
description = "1: this port can be calibrated for low phase uncertainty on link up\
0: this is a standard WR port with regular performance";
prefix = "FEAT_LPC";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
......@@ -179,6 +200,24 @@ peripheral {
access_dev = WRITE_ONLY;
clock = "tx_clk_i";
};
field {
name = "Start calibration of RX timestamper";
prefix = "RX_CAL_START";
description = "write 1: start calibration.\
write 0: no effect";
type = MONOSTABLE;
clock = "rx_clk_i";
};
field {
name = "RX timestamper calibration result flag";
prefix = "RX_CAL_RESULT";
type = BIT;
access_bus = READ_ONLY;
access_dev = WRITE_ONLY;
};
};
reg {
......@@ -258,7 +297,6 @@ peripheral {
11: unqualified port - passes all traffic regardless of VLAN configuration";
type = SLV;
size = 2;
align = 2;
prefix = "Qmode";
access_dev = READ_ONLY;
access_bus = READ_WRITE;
......@@ -404,38 +442,50 @@ peripheral {
prefix = "FCR";
field {
name = "RX Pause enable";
description = "1: enable reception of pause frames and TX path throttling \
0: disable reception of pause frames";
name = "RX Pause 802.3 enable";
description = "1: enable reception of pause frames defined in 802.3 (all priorities) and TX path throttling \
0: disable reception of pause frames defined in 802.3";
prefix = "RXPAUSE";
access_bus = READ_WRITE;
access_dev = READ_WRITE;
access_dev = READ_ONLY;
type = BIT;
load = LOAD_EXT;
};
field {
name = "TX Pause enable";
name = "TX Pause 802.3 enable";
description = "1: enable transmission of pause frames and RX path throttling \
0: disable transmission of pause frames";
prefix = "TXPAUSE";
access_bus = READ_WRITE;
access_dev = READ_WRITE;
access_dev = READ_ONLY;
type = BIT;
};
field {
name = "Rx Pause 802.1Q enable";
description = "1: enable reception of priority-based pause frames (IEEE 802.1Q-2012 Flow Control) \
0: disable reception of priority-based pause frames";
prefix = "RXPAUSE_802_1Q";
access_bus = READ_WRITE;
access_dev = READ_ONLY;
type = BIT;
};
field {
name = "Tx Pause 802.1Q enable (not implemented)";
description = "1: enable transmission of priority-based pause frames (IEEE 802.1Q-2012) Flow Control \
0: disable transmission of priority-based pause frames";
prefix = "TXPAUSE_802_1Q";
access_bus = READ_WRITE;
access_dev = READ_ONLY;
type = BIT;
load = LOAD_EXT;
};
field {
name = "TX pause threshold";
description = "Defines the percentage of space occupied in the RX buffer which triggers the transmission of a PAUSE frame. 0 = empty buffer, 255 = full buffer";
prefix = "TX_THR";
access_bus = READ_WRITE;
access_dev = READ_WRITE;
access_dev = READ_ONLY;
type = SLV;
size = 8;
align = 8;
load = LOAD_EXT;
};
field {
......@@ -443,11 +493,10 @@ peripheral {
description = "Defines the quanta value carried bypause frames sent by the Endpoint";
prefix = "TX_QUANTA";
access_bus = READ_WRITE;
access_dev = READ_WRITE;
access_dev = READ_ONLY;
type = SLV;
size = 16;
align = 16;
load = LOAD_EXT;
};
};
......@@ -598,7 +647,7 @@ peripheral {
prefix = "EN";
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
load = LOAD_EXT;
};
field {
......@@ -610,7 +659,7 @@ peripheral {
align = 16;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
load = LOAD_EXT;
};
};
......@@ -636,30 +685,67 @@ peripheral {
access_bus = READ_WRITE;
};
};
reg {
name = "PCK Injection CTRL";
prefix = "INJ_CTRL";
field {
name = "Config: Interframe GAP";
prefix = "PIC_CONF_IFG";
size = 16;
type = SLV;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
ram {
name = "Event counters memory";
description = "RMON event counters:\
0x0 : TX PCS buffer underruns\
0x4 : RX PCS invalid 8b10b codes\
0x8 : RX PCS sync lost events\
0xc : RX PCS buffer overruns\
0x10: RX CRC errors\
0x14: RX valid frames\
0x18: RX runt frames\
0x1c: RX giant frames\
0x20: RX PCS errors\
0x24: RX dropped frames";
size = 32;
width = 32;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
field {
name = "Config: packet pattern sel id";
prefix = "PIC_CONF_SEL";
size = 3;
type = SLV;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "Config: valid";
prefix = "PIC_CONF_VALID";
type = BIT;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "Mode: packet generate mode";
prefix = "PIC_MODE_ID";
size = 3;
type = SLV;
align= 4;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
prefix = "rmon_ram";
};
field {
name = "Mode: valid";
prefix = "PIC_MODE_VALID";
type = BIT;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
field {
name = "Frame Generation Enabled";
prefix = "PIC_ENA";
type = BIT;
align = 4;
access_dev = READ_WRITE;
access_bus = READ_WRITE;
load = LOAD_EXT;
};
};
};
......@@ -117,7 +117,11 @@ int rts_lock_channel(int channel, int priority);
/* Enabled/disables phase tracking on a particular port */
int rts_enable_ptracker(int channel, int enable);
/* Enabled/disables phase tracking on a particular port */
/* Sets number of raw phase samples the DMTD uses to average to obtain the final phase value.
Lower number = faster measurements (useful during calibration). */
int rts_ptracker_set_average_samples(int channel, int avg_samples);
/* Debug stuff. Reserved for developers. Deliberately left undocumented. */
int rts_debug_command(int param, int value);
#ifdef RTIPC_EXPORT_STRUCTURES
......@@ -179,6 +183,17 @@ static struct minipc_pd rtipc_rts_debug_command_struct = {
},
};
static struct minipc_pd rtipc_rts_set_average_samples_struct = {
.name = "gggg",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int ),
MINIPC_ARG_END
},
};
#endif
#endif
-include ../Makefile.specific
# We are now Kconfig-based
-include ../../.config
......
-include ../Makefile.specific
# We are now Kconfig-based
-include ../../.config
......@@ -6,9 +7,10 @@ OBJS = init.o fpga_io.o util.o pps_gen.o i2c.o shw_io.o i2c_bitbang.o \
i2c_fpga_reg.o pio.o libshw_i2c.o i2c_sfp.o fan.o i2c_io.o hwiu.o \
ptpd_netif.o hal_client.o hal_minirpc.o\
shmem.o rt_client.o \
dot-config.o wrs-msg.o \
dot-config.o config-lpcalib.o wrs-msg.o \
mac.o \
timeout.o
timeout.o \
config-lpcalib.o generic_fsm.o
LIB = libwr.a
......
/*
* White Rabbit parser for Low Phase Drift Calibration configuration
* Copyright (C) 2018, CERN.
*
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Description: re-written parser for configuration file. It allows
* to simultaneously read/write few files.
*
* NOTE: consider merging this with dot-config.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libwr/config-lpcalib.h>
struct key_value
{
char *comment;
char *key;
char *value;
struct key_value *next;
};
struct config_file
{
struct key_value *head;
};
static struct key_value *cfg_find_key(struct config_file *cfg, const char *key, int create)
{
struct key_value *kv = cfg->head, *kv_prev = cfg->head;
while (kv)
{
if (kv->key && !strcasecmp(key, kv->key))
{
return kv;
}
kv_prev = kv;
kv = kv->next;
}
if (!create)
return NULL;
if (!cfg->head)
{
cfg->head = malloc(sizeof(struct key_value));
kv = cfg->head;
}
else
{
kv_prev->next = malloc(sizeof(struct key_value));
kv = kv_prev->next;
}
kv->key = strdup(key);
kv->value = NULL;
kv->comment = NULL;
kv->next = NULL;
return kv;
}
int cfg_get_int(struct config_file *cfg, const char *key, int *value)
{
struct key_value *kv = cfg_find_key(cfg, key, 0);
if (!kv)
return 0;
*value = atoi(kv->value);
return 1;
}
struct config_file *cfg_load(const char *filename, int overwrite)
{
FILE *f = fopen(filename, "rb");
if (!f)
{
if (overwrite)
{
struct config_file *cfg = malloc(sizeof(struct config_file));
f = fopen(filename, "wb");
cfg->head = NULL;
return cfg;
}
return NULL;
}
struct config_file *cfg = malloc(sizeof(struct config_file));
cfg->head = NULL;
while (!feof(f))
{
char str[1024];
int rv = (int)fgets(str, 1024, f);
int i, pos = -1;
if (rv == 0)
break;
for (i = 0; i < strlen(str); i++)
{
if (str[i] == '=' && pos < 0)
pos = i;
if (str[i] == '\n' || str[i] == '\r')
str[i] = 0;
}
struct key_value *kv = malloc(sizeof(struct key_value));
if (pos < 0 || strlen(str) == 0 || str[0] == '#')
{
kv->comment = strdup(str);
kv->key = NULL;
kv->value = NULL;
}
else
{
kv->comment = NULL;
kv->key = strndup(str, pos);
kv->value = strdup(str + pos + 1);
kv->next = NULL;
}
if (!cfg->head)
{
cfg->head = kv;
}
else
{
struct key_value *last = cfg->head;
while (last->next)
last = last->next;
last->next = kv;
}
}
return cfg;
}
int cfg_set_int(struct config_file *cfg, const char *key, int value)
{
struct key_value *kv = cfg_find_key(cfg, key, 1);
if (!kv)
return 0;
char buf[1024];
snprintf(buf, 1024, "%d", value);
kv->value = strdup(buf);
return 1;
}
int cfg_save(struct config_file *cfg, const char *filename)
{
FILE *f = fopen(filename, "wb");
if (!f)
{
printf("Can't open '%s' for writing.\n", filename);
return -1;
}
struct key_value *kv = cfg->head;
while (kv)
{
if (kv->comment)
fprintf(f, "%s\n", kv->comment);
else
fprintf(f, "%s=%s\n", kv->key, kv->value);
kv = kv->next;
}
fclose(f);
return 0;
}
void cfg_close(struct config_file *cfg)
{
struct key_value *kv = cfg->head;
while (kv)
{
if (kv->comment)
free(kv->comment);
if (kv->key)
free(kv->key);
if (kv->value)
free(kv->value);
struct key_value *ptr = kv;
kv = kv->next;
free(ptr);
}
}
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Jean-Claude BAU - CERN
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*
*/
#include <stdlib.h>
#include <string.h>
#include <libwr/wrs-msg.h>
#include <libwr/generic_fsm.h>
static char * fsm_generic_build_events( fsm_t *pfg, int *eventMask );
/* Generic engine used by all ports states machine */
int fsm_generic_run( fsm_t *pfg) {
int ret=0;
int eventMask,isNewState;
fsm_generic_build_events(pfg, &eventMask);
pfg->lastEventMask = eventMask;
isNewState=fsm_has_pending_state(pfg);
/* Check if state has changed */
if ( isNewState ) {
fsm_consume_state(pfg); // update the current state with the scheduled one
}
/* Call state entry */
if (eventMask || isNewState) {
fsm_state_table_entry_t *pt;;
for( pt = pfg->state_table; pt->state >= 0; pt++)
{
if (pt->state == fsm_get_state(pfg) ) {
/* if ( FSM_GEN_DEBUG && pt->fctName!=NULL)
printf("%s: Calling %s (newState=%d, evts=%s),\n",
pfg->fsm_name,
pt->fctName, isNewState,evtStr);*/
ret= (*pt->handler)(pfg,eventMask,isNewState);
if ( fsm_has_pending_state(pfg) ) {
/* Consume state immediately */
return fsm_generic_run(pfg);
}
break;
}
}
}
return ret;
}
static char * fsm_generic_build_events( fsm_t *pfg, int *eventMask)
{
*eventMask=0;
if ( pfg->build_events_handler )
*eventMask=(*pfg->build_events_handler)(pfg);
if ( FSM_GEN_DEBUG ) {
static char str[128];
int copy=*eventMask;
fsm_event_table_entry_t *pe;
str[0]=0;
for( pe = pfg->event_table; pe->evtMask > 0; pe++)
{
if (copy==0) break;
if ( (pe->evtMask & copy) !=0 ) {
if ( str[0]!=0)
strcat(str,"+");
strcat(str,pe->evtName);
copy &=~pe->evtMask;
}
}
return str;
} else {
static char str[1]="";
return str;
}
}
/*
* Fill the FSM structure
* returns 0 on success or -1 on error
*/
int fsm_generic_create(fsm_t *fsm,
const char *name,
fsm_build_event_handler_t build_events_handler,
fsm_state_table_entry_t *state_table,
fsm_event_table_entry_t *event_table,
void *priv)
{
if (fsm->fsm_name!=NULL )
free(fsm->fsm_name); // In case the structure is filled more than one time
fsm->fsm_name = strdup(name);
if ( fsm->fsm_name==NULL )
return -1;
fsm->event_table = event_table;
fsm->state_table = state_table;
fsm->build_events_handler = build_events_handler;
fsm->enabled = 1;
fsm->priv = priv;
fsm_init_state(fsm);
return 0;
}
#ifndef __LIBWR_CONFIG2_H__
#define __LIBWR_CONFIG2_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct config_file;
extern int cfg_get_int(struct config_file *cfg, const char *key, int *value);
extern struct config_file *cfg_load(const char *filename, int overwrite);
extern int cfg_set_int(struct config_file *cfg, const char *key, int value);
extern int cfg_save(struct config_file *cfg, const char *filename);
extern void cfg_free(struct config_file *cfg);
extern void cfg_close(struct config_file *cfg);
#endif /* __LIBWR_CONFIG2_H__ */
/*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Jean-Claude BAU - CERN
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*
*
*/
#ifndef GENERIC_FSM_H
#define GENERIC_FSM_H
#include <stdio.h>
#include <string.h>
#define FSM_GEN_DEBUG 0
#define FSM_SET_FCT_NAME(name) .handler=name, .fctName=#name
/*
* Used to define the callback to call for a givent state
*/
struct fsm_s;
/* States used by the generic FSM */
typedef struct {
int state;
int nextState;
} fsm_state_t;
typedef int (*fsm_state_handler_t)( struct fsm_s* fsm, int eventMsk, int newState);
typedef int (*fsm_build_event_handler_t)( struct fsm_s *fsm );
typedef struct {
int state;
char * stateName;
fsm_state_handler_t handler;
char *fctName;
} fsm_state_table_entry_t;
typedef struct {
int evtMask;
char * evtName;
} fsm_event_table_entry_t;
/*
* Structure containing all information needed by the state machine
*/
typedef struct fsm_s {
char * fsm_name;
fsm_build_event_handler_t build_events_handler;
fsm_state_table_entry_t *state_table;
fsm_event_table_entry_t *event_table;
fsm_state_t st;
int enabled;
int lastEventMask;
void *priv;
} fsm_t;
/* returns current state of the FSM */
static inline int fsm_get_state(fsm_t *fsm)
{
return fsm->st.state;
}
/* sets the current (immediate) state of the FSM */
static inline int fsm_set_state(fsm_t *fsm, int newState)
{
fsm->st.state = newState;
return fsm->st.state;
}
/* Set state and next state to -1 */
static inline int fsm_init_state(fsm_t *fsm)
{
fsm->st.state = fsm->st.nextState=-1;
return fsm->st.state;
}
/* gets the next (scheduled) state of the FSM */
static inline int fsm_get_next_state(fsm_t *fsm)
{
return fsm->st.nextState;
}
/* returns a human-readable state name for a given state */
static inline const char * fsm_get_state_name(fsm_t *fsm)
{
static const char *unknown_msg = "<Unknown>";
static const char *not_init_msg = "<NotInit>";
int st = fsm_get_state(fsm);
if (st == -1 )
return not_init_msg;
if (st < -1 )
return unknown_msg;
fsm_state_table_entry_t *pt = &fsm->state_table[ fsm_get_state(fsm) ];
return pt->stateName;
}
static inline const char *fsm_get_event_mask_as_string(fsm_t *fsm)
{
static char str[1024];
fsm_event_table_entry_t *evt;
int lastEventMask=fsm->lastEventMask;
str[0]=0;
for( evt = fsm->event_table; evt->evtMask > 0; evt++ )
{
if ( lastEventMask == 0 ) break;
if (lastEventMask & evt->evtMask)
{
if ( str[0]!=0 )
strcat(str," ");
strcat(str, evt->evtName);
lastEventMask &=~evt->evtMask;
}
}
if (lastEventMask!=0)
strcat(str," ???");
return str;
}
/* updates the current state of the FSM with the next (scheduled) state */
static inline void fsm_consume_state(fsm_t *fsm)
{
fsm->st.state = fsm_get_next_state(fsm);
if (FSM_GEN_DEBUG)
printf("%s: Enter state %s\n", fsm->fsm_name, fsm_get_state_name(fsm));
}
/* schedules next state of the FSM to newState */
static inline void fsm_fire_state(fsm_t *fsm, int newState)
{
fsm->st.nextState=newState;
}
/* returns nonzero if there's a next (scheduled) state present */
static inline int fsm_has_pending_state(fsm_t *fsm)
{
return fsm_get_state(fsm) != fsm_get_next_state(fsm);
}
/* prototypes */
/* Does a single turn of the FSM */
int fsm_generic_create(fsm_t *fsm, const char *name,
fsm_build_event_handler_t build_events_handler,
fsm_state_table_entry_t *state_table,
fsm_event_table_entry_t *event_table, void *priv);
int fsm_generic_run ( fsm_t *fsm );
#endif
#ifndef __LIBWR_HAL_SHMEM_H__
#define __LIBWR_HAL_SHMEM_H__
#include <string.h>
#include <hal_exports.h>
#include <libwr/sfp_lib.h>
#include <string.h>
#include <libwr/generic_fsm.h>
#include <libwr/timeout.h>
/* Port state machine states */
#define HAL_PORT_STATE_DISABLED 0
#define HAL_PORT_STATE_LINK_DOWN 1
#define HAL_PORT_STATE_UP 2
#define HAL_PORT_STATE_CALIBRATION 3
#define HAL_PORT_STATE_LOCKING 4
#define HAL_PORT_STATE_RESET 5
typedef enum {
HAL_PORT_STATE_INIT=0,
HAL_PORT_STATE_DISABLED,
HAL_PORT_STATE_LINK_DOWN,
HAL_PORT_STATE_LINK_UP,
} halPortState_t;
/* Read temperature from SFPs */
#define READ_SFP_DIAG_ENABLE 1
......@@ -61,31 +65,64 @@ typedef struct hal_port_calibration {
struct shw_sfp_dom sfp_dom_raw;
} hal_port_calibration_t;
/* Low Phase Drift Calibration for tx */
typedef struct {
int attempts;
int cal_saved_phase;
int cal_saved_phase_valid;
int measured_phase;
int expected_phase;
int tolerance;
int update_cnt;
int expected_phase_valid;
timeout_t calib_timeout;
}halPortLpdcTx_t;
/* Low Phase Drift Calibration for rx */
typedef struct {
timeout_t link_timeout;
timeout_t align_timeout;
timeout_t earlyup_timeout;
timeout_t align_to_link_timeout;
timeout_t restart_timeout;
int attempts;
}halPortLpdcRx_t;
typedef struct {
int numberOfLpdcPorts;
uint32_t maskUsedPorts;
uint32_t maskTxSetupDonePorts;
int firstLpdcPort;
int lastLpdcPort;
int calFileSynced;
}halGlobalLPDC_t; /* Global data for Low phase drift calibration */
typedef struct {
int isSupported; /* Set if Low Phase Drift Calibration is supported */
fsm_t txSetupFSM;
fsm_t rxSetupFSM;
timeout_t minCalibRx_timeout;
halPortLpdcTx_t *txSetup;
halPortLpdcRx_t *rxSetup;
halGlobalLPDC_t *globalLpdc;
}halPortLPDC_t; /* per-port data for Low phase drift calibration */
/* Internal port state structure */
struct hal_port_state {
/* non-zero: allocated */
int in_use;
/* linux i/f name */
char name[16];
/* MAC addr */
uint8_t hw_addr[6];
int in_use; /* non-zero: allocated */
char name[16]; /* linux i/f name */
uint8_t hw_addr[6]; /* MAC addr */
int hw_index; /* ioctl() hw index : 0..n */
/* ioctl() hw index */
int hw_index;
/* file descriptor for ioctls() */
int fd;
int fd; /* file descriptor for ioctls() */
int hw_addr_auto;
/* port FSM state (HAL_PORT_STATE_xxxx) */
int state;
/* fiber type, used to get alpha for SFP frequency */
int fiber_index;
fsm_t fsm;
fsm_t pllFsm;
/* 1: PLL is locked to this port */
int locked;
int fiber_index;/* fiber type, used to get alpha for SFP frequency */
int locked; /* 1: PLL is locked to this port */
/* calibration data */
hal_port_calibration_t calib;
......@@ -94,11 +131,10 @@ struct hal_port_state {
uint32_t phase_val;
int phase_val_valid;
int tx_cal_pending, rx_cal_pending;
/* locking FSM state */
int lock_state;
/*reference lock period in picoseconds*/
uint32_t clock_period;
int lock_state; /* locking FSM state */
uint32_t clock_period; /*reference lock period in picoseconds*/
/* approximate DMTD phase value (on slave port) at which RX timestamp
* (T2) counter transistion occurs (picoseconds) */
......@@ -108,11 +144,12 @@ struct hal_port_state {
* counter transistion occurs (picoseconds) */
uint32_t t4_phase_transition;
/* Endpoint's base address */
uint32_t ep_base;
uint32_t ep_base;/* Endpoint's base address */
/* whether SFP has diagnostic Monitoring capability */
int has_sfp_diag;
/* True if SFP is inserted */
int sfpPresent;
/* whether the port shall be monitored by SNMP */
int monitor;
......@@ -122,6 +159,14 @@ struct hal_port_state {
int synchronized; // <>0 if port is synchronized
int portInfoUpdated; // Set to 1 when updated
/* Events to process */
int evt_reset; /* Set if a reset is requested */
int evt_lock; /* Set if the ptracker must be activated*/
int evt_linkUp; /* Set if link is up ( driver call )*/
int evt_powerDown; /* Set if port is in power down state (MII MCR.PDOWN ==1 ) */
/* Low phase drift calibration data */
halPortLPDC_t lpdc;
};
struct hal_temp_sensors {
......@@ -132,21 +177,31 @@ struct hal_temp_sensors {
};
/* This is the overall structure stored in shared memory */
#define HAL_SHMEM_VERSION 12 /* Version 12, added monitor to
struct hal_port_state */
#define HAL_SHMEM_VERSION 15 /* Add share memory state*/
typedef enum {
HAL_SHMEM_STATE_NOT_INITITALIZED=0,
HAL_SHMEM_STATE_INITITALIZING,
HAL_SHMEM_STATE_INITITALIZED
}halShmemState_t;
struct hal_shmem_header {
int nports;
int hal_mode;
halShmemState_t shmemState;
struct hal_port_state *ports;
struct hal_temp_sensors temp;
int read_sfp_diag;
};
static inline int state_up(int state)
static inline int get_port_state(struct hal_port_state *ps)
{
return (state != HAL_PORT_STATE_LINK_DOWN
&& state != HAL_PORT_STATE_DISABLED);
return fsm_get_state( &ps->fsm );
}
static inline int state_up(struct hal_port_state *ps)
{
return get_port_state(ps) == HAL_PORT_STATE_LINK_UP;
}
static inline struct hal_port_state *hal_lookup_port(
......@@ -154,10 +209,14 @@ static inline struct hal_port_state *hal_lookup_port(
const char *name)
{
int i;
for (i = 0; i < nports; i++)
if (ports[i].in_use && (!strcmp(name, ports[i].name)))
return ports + i;
struct hal_port_state *p=ports;
for (i = 0; i < nports; i++) {
if (p->in_use && (!strcmp(name, p->name))) {
return p;
}
p++;
}
return NULL;
}
......
......@@ -2,10 +2,6 @@
#define __LIBWR_SHW_SFPLIB_H
/* note each led contains green and orange part */
#define SFP_LED_WRMODE_SLAVE (1) /* green */
#define SFP_LED_WRMODE_OTHER (2) /* orange */
#define SFP_LED_WRMODE_MASTER (3) /* yellow */
#define SFP_LED_WRMODE_OFF (4) /* to off entire WRMODE LED */
#define SFP_LED_WRMODE1 (1 << 0)
#define SFP_LED_WRMODE2 (1 << 1)
#define SFP_LED_SYNCED (1 << 2)
......
......@@ -238,7 +238,7 @@ struct wr_socket *ptpd_netif_create_socket(int sock_type, int flags,
hwconfig.tx_type = HWTSTAMP_TX_ON;
hwconfig.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
ifr.ifr_data = &hwconfig;
ifr.ifr_data = (char *)&hwconfig;
if (ioctl(fd, SIOCSHWTSTAMP, &ifr) < 0) {
perror("SIOCSHWTSTAMP");
......
......@@ -71,6 +71,19 @@ int rts_set_mode(int mode)
return rval;
}
int rts_ptracker_set_average_samples(int channel, int avg_samples)
{
int rval;
int ret = minipc_call(client, RTS_TIMEOUT,
&rtipc_rts_set_average_samples_struct, &rval, avg_samples);
if (ret < 0)
return ret;
return rval;
}
/* Sets the phase setpoint on a given channel */
int rts_adjust_phase(int channel, int32_t phase_setpoint)
{
......
Subproject commit 28dff05538f48b5eb68228e5a6db7712bf8b9795
*.o
*.a
*~
\ No newline at end of file
This diff is collapsed.
-include ../Makefile.specific
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -O2 -fno-strict-aliasing
LDFLAGS = -L. -lminipc -lm
# We need to support freestading environments: an embedded CPU that
# sits as a server on its own memory are and awaits commands
# The user can pass IPC_FREESTANDING=y
IPC_FREESTANDING ?= $(shell ./check-freestanding $(CC))
IPC_HOSTED ?= $(shell ./check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -Iarch-$(ARCH)
else
IPC_HOSTED = y
endif
OBJ-$(IPC_HOSTED) = minipc-core.o minipc-server.o minipc-client.o
OBJ-$(IPC_FREESTANDING) = minipc-mem-server.o
LIB = libminipc.a
# export these to the examples (but if you make there IPC_HOSTED is default
export IPC_FREESTANDING IPC_HOSTED
all: $(LIB)
$(MAKE) -C examples
$(LIB): $(OBJ-y)
$(AR) r $@ $^
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(wildcard *.o): $(wildcard *.h)
clean:
rm -f *.o *~ $(LIB)
$(MAKE) -C examples clean
install:
@echo "We have no install rule by now"
All documentation is now under the doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
#ifndef __ARCH_LM32_STDINT_H__
#define __ARCH_LM32_STDINT_H__
/*
* We miss a stdint.h in our compiler, so provide some types here,
* knowing the CPU is 32-bits and uses LP32 model
*/
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int32_t;
#endif /* __ARCH_LM32_STDINT_H__ */
#!/bin/sh
# I used to just check __STDC_HOSTED__ without special CFLAGS, but now I
# want to force freestanding compilation on otherwise hosted processors.
# Thus, the user can set IPC_FREESTANDING=y . If unset, this gets called.
# Accept "-n" to invert the result (select second field, not first)
if [ "x$1" = "x-n" ]; then select=2; shift; else select=1; fi
CC=$1
if [ "x$CC" = "x" ]; then
echo "$0: pass the compiler path (\$CC) as argument" >& 2
exit 1
fi
# Check the compiler: if it has no matches is prints the unadorned string
if [ "$($CC -print-file-name=libc.so)" = "libc.so" ]; then
res=y,n
else
res=n,y
fi
# If passed from the user, override the autodetected value
if [ "$IPC_FREESTANDING" = "y" ]; then
res=y,n
fi
echo $res | cut -d, -f $select
*~
*.aux
*.cp
*.fn
*.html
*.info
*.ky
*.log
*.pdf
*.pg
*.texi
*.toc
*.tp
*.txt
*.vr
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
%.texi: %.in
@rm -f $@
sed -f ./infofilter $< > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
install:
;; use:
;; emacs -batch -l ./fixinfo.el <file>
;; or, better:
;; emacs -batch --no-site-file -l ./fixinfo.el <file>
(defun fixinfo (file)
(find-file-other-window file)
(message (concat "Maxing texinfo tree in " file))
(texinfo-all-menus-update)
(texinfo-every-node-update)
(save-buffer)
(kill-buffer (current-buffer))
)
;; loop over command line arguments
(mapcar 'fixinfo command-line-args-left)
(kill-emacs)
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
#preserve blanks and braces in @example blocks
/@example/,/@end example/ s/{/@{/g
/@example/,/@end example/ s/}/@}/g
/@example/,/@end example/ p
/@example/,/@end example/ d
/@smallexample/,/@end smallexample/ s/{/@{/g
/@smallexample/,/@end smallexample/ s/}/@}/g
/@smallexample/,/@end smallexample/ p
/@smallexample/,/@end smallexample/ d
# remove leading blanks
s/^[ ]*//
This diff is collapsed.
trivial-server
trivial-client
pty-server
pty-client
mbox-bridge
mbox-client
mbox-process
shmem-server
shmem-client
freestanding-server
# examples for mini-rpc; depends on ..
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -I.. -O2
LDFLAGS = -L.. -lminipc -lm
# we may be hosted or freestanding. For freestanding there is one
# example only. The following variables are exported by the upper
# Makefile, but if you "make" in this directory, it checks by itself
IPC_FREESTANDING ?= $(shell ../check-freestanding $(CC))
IPC_HOSTED ?= $(shell ../check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -I../arch-$(ARCH)
else
IPC_HOSTED = y
endif
PROGS-$(IPC_HOSTED) = trivial-server trivial-client
PROGS-$(IPC_HOSTED) += pty-server pty-client
PROGS-$(IPC_HOSTED) += mbox-process mbox-bridge mbox-client
PROGS-$(IPC_HOSTED) += shmem-server shmem-client
IPC_FREESTANDING ?= n
PROGS-$(IPC_FREESTANDING) += freestanding-server
all: $(PROGS-y)
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
pty-server: pty-server.o pty-rpc_server.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -lutil -o $@
pty-client: pty-client.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-server: shmem-server.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-client: shmem-client.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(PROGS-y) $(wildcard *.o): $(wildcard ../*.h ../*.a)
clean:
rm -f *.o *~ $(PROGS)
All documentation is now under the ../doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
/*
* Mini-ipc: an example freestanding server, based in memory
*
* Copyright (C) 2011,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This code is copied from trivial-server, and made even more trivial
*/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "minipc.h"
/* A function that ignores the RPC and is written normally */
static int ss_do_sum(int a, int b)
{
return a + b;
}
/* The following ones are RPC-aware */
static int ss_sum_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int i;
i = ss_do_sum(args[0], args[1]);
*(int *)ret = i;
return 0;
}
static int ss_mul_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int a, b;
a = *(int *)args;
b = *(int *)(args + 1);
*(int *)ret = a * b;
return 0;
}
/* Describe the two functions above */
const struct minipc_pd ss_sum_struct = {
.f = ss_sum_function,
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_mul_struct = {
.f = ss_mul_function,
.name = "mul",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *server;
server = minipc_server_create("mem:f000", 0);
if (!server)
return 1;
minipc_export(server, &ss_sum_struct);
minipc_export(server, &ss_mul_struct);
while (1) {
/* do something else... */
minipc_server_action(server, 1000);
}
}
/*
* Example bridge beteen mini-ipc and a mailbox memory area
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This example uses a shared memory area to talk with mbox-process.
* The same technique can be used to communicate with an os-less
* soft-core within an FPGA, and actually this is the reason why I'm
* writing this demo.
*
* The bridge can export functions in both ways: as a mini-ipc server
* it exports "stat", and as a client it calls gettimeofday in the
* trivial-server.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/select.h>
#include "minipc.h"
#include "minipc-shmem.h" /* The shared data structures */
struct minipc_mbox_info *info; /* unfortunately global... */
/* This function implements the "stat" mini-ipc server, by asking mbox */
static int mb_stat_server(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *fname = (void *)args;
/* pass this to the shmem */
memset(info->fname, 0, sizeof(info->fname));
strncpy(info->fname, fname, sizeof(info->fname) -1 );
/* ask and wait */
info->bridge_req = 1;
while (info->bridge_req)
usleep(1000);
memcpy(ret, &info->stbuf, sizeof(info->stbuf));
return 0;
}
/* The description here is the same as in the server */
const struct minipc_pd mb_tod_struct = {
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
/* And here it's the same as in the client */
struct minipc_pd mb_stat_struct = {
.f = mb_stat_server,
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* No arguments */
int main(int argc, char **argv)
{
struct minipc_ch *server;
struct minipc_ch *client;
void *shmem;
int ret;
if (argc > 1) {
fprintf(stderr, "%s: no arguments please\n", argv[0]);
exit(1);
}
/* Create your shared memory and/or attach to it */
ret = shmget(MINIPC_SHM, sizeof(struct minipc_mbox_info),
IPC_CREAT | 0666);
if (ret < 0) {
fprintf(stderr, "%s: shmget(): %s\n", argv[0],
strerror(errno));
exit(1);
}
shmem = shmat(ret, NULL, SHM_RND);
if (shmem == (void *)-1) {
fprintf(stderr, "%s: shmat(): %s\n", argv[0],
strerror(errno));
exit(1);
}
info = shmem;
/* Create a server socket for mini-ipc */
server = minipc_server_create("mbox", 0);
if (!server) {
fprintf(stderr, "%s: server_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(server, stderr);
minipc_export(server, &mb_stat_struct);
/* Connect as a client to the trivial-server */
client = minipc_client_create("trivial", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Loop serving both mini-ipc and the mailbox */
while (1) {
fd_set set;
struct timeval to = {
MBOX_POLL_US / (1000*1000),
MBOX_POLL_US % (1000*1000)
};
minipc_server_get_fdset(server, &set);
/* Wait for any server, with the defined timeout */
ret = select(16 /* hack */, &set, NULL, NULL, &to);
if (ret > 0) {
if (minipc_server_action(server, 0) < 0) {
fprintf(stderr, "%s: server_action(): %s\n",
argv[0], strerror(errno));
exit(1);
}
}
/* No IPC request: if needed act as IPC client */
if (info->proc_req) {
memset(&info->tv, 0, sizeof(info->tv));
minipc_call(client, 100 /* ms */,
&mb_tod_struct, &info->tv, NULL);
info->proc_req = 0;
}
}
}
/*
* Example mbox client
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*
* This process requests "stat" calls to the mbox-bridge, which in turn
* asks it to the "remote" process that lives behing an mbox (shmem).
* The program reads filenames from stdin, in order to allow several clients
* connected to the same bridge.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include "minipc.h"
#define CLIENT_TIMEOUT 100 /* ms */
/* The description here is the same as in the server */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *client;
struct stat stbuf;
int ret;
char s[80];
client = minipc_client_create("mbox", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
while (fgets(s, sizeof(s), stdin)) {
/* strip trailing newline */
s[strlen(s)-1] = '\0';
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat,
&stbuf, s);
if (ret < 0) {
fprintf(stderr, "stat(%s): %s\n", s, strerror(errno));
continue;
}
printf("%s:\n", s);
printf("mode %o, size %li, owner %i, atime %li\n",
stbuf.st_mode, (long)stbuf.st_size, stbuf.st_uid,
stbuf.st_atime);
}
return 0;
}
/*
* Example process connected to the world through a mailbox
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This example uses a shared memory area to talk with the mbox-bridge.
* In my real-world application this will be an os-less
* soft-core within an FPGA.
*
* The process is both a server and a client. As a server iIt replies
* to "stat" calls coming from the bridge process; as a client it asks
* the time of day once a second or so.
*
* It doesn't need minipc.h because it is not directly connected to the ipc
* mechanism
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc-shmem.h" /* The shared data structures */
struct minipc_mbox_info *info; /* unfortunately global... */
/* No arguments */
int main(int argc, char **argv)
{
void *shmem;
int ret, i;
if (argc > 1) {
fprintf(stderr, "%s: no arguments please\n", argv[0]);
exit(1);
}
/* Create your shared memory and/or attach to it */
ret = shmget(MINIPC_SHM, sizeof(struct minipc_mbox_info),
IPC_CREAT | 0666);
if (ret < 0) {
fprintf(stderr, "%s: shmget(): %s\n", argv[0],
strerror(errno));
exit(1);
}
shmem = shmat(ret, NULL, SHM_RND);
if (shmem == (void *)-1) {
fprintf(stderr, "%s: shmat(): %s\n", argv[0],
strerror(errno));
exit(1);
}
info = shmem;
/* Loop forever */
for (i = 0; 1; i++) {
if (info->bridge_req) {
/* We got a request: serve it */
printf("Serving stat(%s)\n", info->fname);
memset(&info->stbuf, 0, sizeof(info->stbuf));
stat(info->fname, &info->stbuf);
info->bridge_req = 0;
}
if (i * MBOX_POLL_US >= 1000 * 1000) {
/* 1s passed, ask for the date */
info->proc_req = 1;
while (info->proc_req)
;
printf("time: %li.%06li\n", (long)info->tv.tv_sec,
(long)info->tv.tv_usec);
i = 0;
}
usleep(MBOX_POLL_US);
}
}
#ifndef __MINIPC_SHMEM_H__
#define __MINIPC_SHMEM_H__
#include <sys/stat.h>
#include <sys/time.h>
#define MINIPC_SHM 0x3465 /* Random */
struct minipc_mbox_info {
volatile int bridge_req; /* A request is asked by the bridge */
volatile int proc_req; /* A request is asked by the process */
/* The bridge can ask "stat" to the bare process */
char fname[64];
struct stat stbuf;
/* The bare process can ask timeofday to the bridge */
struct timeval tv;
};
#define MBOX_POLL_US (10*1000) /* 10ms */
#endif /* __MINIPC_SHMEM_H__ */
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "minipc.h"
#include "pty-server.h"
#define CLIENT_TIMEOUT 200 /* ms */
/* Retrieve the counters of the pty file descriptors */
static int do_count(struct minipc_ch *client, char **argv)
{
int ret;
struct pty_counts c;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_count, &c);
if (ret < 0)
return ret;
printf("counts: %i %i\n", c.in, c.out);
return 0;
}
/* getenv and setenv in the remote process */
static int do_getenv(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_getenv, buf, argv[2]);
if (ret < 0)
return ret;
printf("getenv(%s) = %s\n", argv[2], buf);
return 0;
}
static int do_setenv(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_setenv, &ret2, argv[2],
argv[3]);
if (ret < 0)
return ret;
printf("setenv %s = %s : success\n", argv[2], argv[3]);
return 0;
}
/* send a string to the subshell of the server */
static int do_feed(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_feed, &ret2, argv[2]);
if (ret < 0)
return ret;
printf("feeded %s : success\n", argv[2]);
return 0;
}
/* calculate a remote strlen */
static int do_strlen(struct minipc_ch *client, char **argv)
{
int ret, len;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strlen, &len, argv[2]);
if (ret < 0)
return ret;
printf("strlen(%s) = %i\n", argv[2], len);
return 0;
}
/* calculate a remote strcat */
static int do_strcat(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strcat, buf,
argv[2], argv[3]);
if (ret < 0)
return ret;
printf("strcat(%s,%s) = %s\n", argv[2], argv[3], buf);
return 0;
}
/* ask for a remote stat on a passed filename */
static int do_stat(struct minipc_ch *client, char **argv)
{
int ret;
struct stat stbuf;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat, &stbuf,
argv[2]);
if (ret < 0)
return ret;
printf("stat(\"%s\"):\n", argv[2]);
printf("dev %04x, inode %i, mode %o (rdev %04x), size %i\n",
(int)stbuf.st_dev, (int)stbuf.st_ino, stbuf.st_mode,
(int)stbuf.st_rdev, (int)stbuf.st_size);
return 0;
}
/*
* This is a parsing table for argv[1]
*/
struct {
char *name;
int (*f)(struct minipc_ch *client, char **argv);
int argc;
} *cp, calls[] = {
{ "count", do_count, 2},
{ "getenv", do_getenv, 3},
{ "setenv", do_setenv, 4},
{ "feed", do_feed, 3},
{ "strlen", do_strlen, 3},
{ "strcat", do_strcat, 4},
{ "stat", do_stat, 3},
{NULL, },
};
/*
* And this is the trivial main function -- more error checking than code
*/
int main(int argc, char **argv)
{
struct minipc_ch *client;
if (argc < 2) {
fprintf(stderr, "%s: use \"%s <cmd> [<arg> ...]\"\n",
argv[0], argv[0]);
exit(1);
}
client = minipc_client_create(PTY_RPC_NAME, 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* scan the table */
for (cp = calls; cp->name; cp++)
if (!strcmp(argv[1], cp->name))
break;
if (!cp->name) {
fprintf(stderr, "%s: no such command \"%s\"\n", argv[0],
argv[1]);
exit(1);
}
if (argc != cp->argc) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
argv[0], argv[1]);
exit(1);
}
/* run the RPC we found */
if (cp->f(client, argv) < 0) {
fprintf(stderr, "%s: remote \"%s\": %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
exit(0);
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file includes the RPC functions exported by the server.
* RPC-related stuff has been split to a different file for clarity.
*/
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc.h"
#include "pty-server.h"
static int saved_fdm; /* file descriptor of pty master, used by "feed" */
static struct pty_counts *saved_pc; /* counters, used by "count" */
/* Return the count accessing the pointer to the structure within main() */
static int pty_server_do_count(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
*(struct pty_counts *)ret = *saved_pc;
return 0;
}
/* Run getenv and setenv on behalf of the client */
static int pty_server_do_getenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *res = getenv(envname);
strcpy(ret, res); /* FIXME: max size */
return 0;
}
static int pty_server_do_setenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *envval;
args = minipc_get_next_arg(args, pd->args[0]);
envval = (void *)args;
setenv(envname, envval, 1);
return 0;
}
/* feed a string to the sub-shell (adding a newline) */
static int pty_server_do_feed(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *command = (void *)args;
int wrote;
wrote = write(saved_fdm, command, strlen(command));
wrote += write(saved_fdm, "\n", 1);
return 0;
}
/* calculate strlen for a string of the client */
static int pty_server_do_strlen(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s = (void *)args;
*(int *)ret = strlen(s);
return 0;
}
/* strcat two remote strings and pass the result back */
static int pty_server_do_strcat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s, *t;
char scat[256];
s = (void *)args;
args = minipc_get_next_arg(args, pd->args[0]);
t = (void *)args;
strncpy(scat, s, sizeof(scat));
strncat(scat, t, sizeof(scat) - strlen(s));
strcpy(ret, scat); /* FIXME: max size */
return 0;
}
/* stat a pathname received from the remote party */
static int pty_server_do_stat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct stat stbuf;
char *fname = (void *)args;
if (stat(fname, &stbuf) < 0)
return -1;
memcpy(ret, &stbuf, sizeof(stbuf));
return 0;
}
/*
* The following is called by the main function, and exports stuff.
* All the rest happens through callbacks from the library
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
int pty_export_functions(struct minipc_ch *ch, int fdm, struct pty_counts *pc)
{
int i;
/* save data for the callbacks */
saved_fdm = fdm;
saved_pc = pc;
/* Use a handy table for the registration loop */
static struct {
struct minipc_pd *desc;
minipc_f *f;
} export_list [] = {
{&rpc_count, pty_server_do_count},
{&rpc_getenv, pty_server_do_getenv},
{&rpc_setenv, pty_server_do_setenv},
{&rpc_feed, pty_server_do_feed},
{&rpc_strlen, pty_server_do_strlen},
{&rpc_strcat, pty_server_do_strcat},
{&rpc_stat, pty_server_do_stat},
};
/*
* Complete the data structures by filling the function pointers
* and then register each of the exported procedures
*/
for (i = 0; i < ARRAY_SIZE(export_list); i++) {
export_list[i].desc->f = export_list[i].f;
if (minipc_export(ch, export_list[i].desc) < 0)
return -1;
}
return 0;
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file defines the data structures used to describe the RPC calls
* between server and client. Note that the function pointers are only
* instantiated in the server
*/
#include <sys/stat.h> /* for the structure */
#include "minipc.h"
#include "pty-server.h"
/* retrieve in and out counts from the pty-server */
struct minipc_pd rpc_count = {
.name = "count",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct pty_counts),
.args = {
MINIPC_ARG_END,
},
};
/* run getenv in the server */
struct minipc_pd rpc_getenv = {
.name = "getenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run setenv in the server */
struct minipc_pd rpc_setenv = {
.name = "setenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* feed a string to the underlying shell */
struct minipc_pd rpc_feed = {
.name = "feed",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* horribly run strlen and strcat */
struct minipc_pd rpc_strlen = {
.name = "strlen",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
struct minipc_pd rpc_strcat = {
.name = "strcat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run "stat" on a file name */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This is the pty-server. It only does the pty-master work, all RPC stuff
* has been confined to prt-rpc_server.c, which is called by our main loop
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pty.h>
#include <fcntl.h>
#include <utmp.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include "minipc.h"
#include "pty-server.h"
int main(int argc, char **argv)
{
int fdm, fds, pid, exitval = 0;
struct minipc_ch *ch;
struct pty_counts counters = {0,};
/* First, open the pty */
if (openpty(&fdm, &fds, NULL, NULL, NULL) < 0) {
fprintf(stderr, "%s: openpty(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Run a shell and let it go by itself before we open the rpc */
fprintf(stderr, "%s: Running a sub-shell in a new pty\n", argv[0]);
if ((pid = fork()) < 0) {
fprintf(stderr, "%s: fork(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (!pid) {
/* Child: become a shell and disappear... */
close(fdm);
login_tty(fds);
execl("/bin/sh", "sh", NULL);
fprintf(stderr, "%s: exec(/bin/sh): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Open the RPC server channel */
ch = minipc_server_create(PTY_RPC_NAME, 0);
if (!ch) {
fprintf(stderr, "%s: rpc_open(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Log file for diagnostics */
{
char name[] = "/tmp/pty-server.XXXXXX";
int logfd;
FILE *logf;
logfd = mkstemp(name);
if (logfd >= 0) {
logf = fdopen(logfd, "w");
if (logf)
minipc_set_logfile(ch, logf);
}
}
/* Register your functions: all our RPC is split to another source */
if (pty_export_functions(ch, fdm, &counters)) {
fprintf(stderr, "%s: exporting RPC functions: %s\n", argv[0],
strerror(errno));
exit(1);
}
/*
* Now, we must mirror stdin/stdout to the pty master, with RPC too.
* The first step is horribly changing the termios of our tty
*/
close(fds);
if (system("stty raw -echo") < 0) {
fprintf(stderr, "%s: can't run \"stty\"\n", argv[0]);
exit(1);
}
while (waitpid(pid, NULL, WNOHANG) != pid) {
fd_set set;
int nfd, i, j;
char buf[256];
/* ask the RPC engine its current fdset and augment it */
minipc_server_get_fdset(ch, &set);
FD_SET(STDIN_FILENO, &set);
FD_SET(fdm, &set);
/* wait for any of the FD to be active */
nfd = select(64 /* Hmmm... */, &set, NULL, NULL, NULL);
if (nfd < 0 && errno == EINTR)
continue;
if (nfd < 0) {
fprintf(stderr, "%s: select(): %s\n", argv[0],
strerror(errno));
exitval = 1;
break;
}
/* Handle fdm and fds by just mirroring stuff and counting */
if (FD_ISSET(STDIN_FILENO, &set)) {
i = read(0, buf, sizeof(buf));
if (i > 0) {
counters.in += i;
do {
j = write(fdm, buf, i);
i -= j;
} while (i && j >= 0);
}
nfd--;
}
if (FD_ISSET(fdm, &set)) {
i = read(fdm, buf, sizeof(buf));
if (i > 0) {
counters.out += i;
do {
j = write(1, buf, i);
i -= j;
} while (i && j >= 0);
}
nfd--;
}
/* If there are no more active fd, loop over */
if (!nfd)
continue;
/*
* If we are there, there has been an RPC call.
* We tell the library to use a 0 timeout, since we know
* for sure that at least one of its descriptors is pending.
*/
minipc_server_action(ch, 0);
}
/* The child shell exited, reset the tty and exit. Let RPC die out */
if (system("stty sane") < 0)
fprintf(stderr, "%s: can't restore tty settings\n", argv[0]);
exit(exitval);
}
#include "minipc.h"
struct pty_counts {
int in, out;
};
#define PTY_RPC_NAME "pty-server"
/* structures are in pty-rpc_structs.c */
extern struct minipc_pd
rpc_count, rpc_getenv, rpc_setenv, rpc_feed, rpc_strlen, rpc_strcat, rpc_stat;
/* The server needs to call out to a different file */
extern int pty_export_functions(struct minipc_ch *ch,
int fdm, struct pty_counts *pc);
/*
* Example mini-ipc client for shmem
* Mostly copied and adapted from pty-client.c
*
* Copyright (C) 2011,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include "minipc.h"
#include "shmem-structs.h"
#define CLIENT_TIMEOUT 200 /* ms */
/* getenv and setenv in the remote process */
static int do_getenv(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_getenv, buf, argv[2]);
if (ret < 0)
return ret;
printf("getenv(%s) = %s\n", argv[2], buf);
return 0;
}
static int do_setenv(struct minipc_ch *client, char **argv)
{
int ret, ret2;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_setenv, &ret2, argv[2],
argv[3]);
if (ret < 0)
return ret;
printf("setenv %s = %s : success\n", argv[2], argv[3]);
return 0;
}
static int do_add(struct minipc_ch *client, char **argv)
{
int ret;
int a = atoi(argv[2]);
int b = atoi(argv[3]);
int sum;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_add, &sum, a, b);
if (ret < 0)
return ret;
printf("add %i + %i = %i : success\n", a, b, sum);
return 0;
}
/* calculate a remote strlen */
static int do_strlen(struct minipc_ch *client, char **argv)
{
int ret, len;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strlen, &len, argv[2]);
if (ret < 0)
return ret;
printf("strlen(%s) = %i\n", argv[2], len);
return 0;
}
/* calculate a remote strcat */
static int do_strcat(struct minipc_ch *client, char **argv)
{
int ret;
char buf[256]; /* FIXME: size limits? */
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_strcat, buf,
argv[2], argv[3]);
if (ret < 0)
return ret;
printf("strcat(%s,%s) = %s\n", argv[2], argv[3], buf);
return 0;
}
/* ask for a remote stat on a passed filename */
static int do_stat(struct minipc_ch *client, char **argv)
{
int ret;
struct stat stbuf;
ret = minipc_call(client, CLIENT_TIMEOUT, &rpc_stat, &stbuf,
argv[2]);
if (ret < 0)
return ret;
printf("stat(\"%s\"):\n", argv[2]);
printf("dev %04x, inode %i, mode %o (rdev %04x), size %i\n",
(int)stbuf.st_dev, (int)stbuf.st_ino, stbuf.st_mode,
(int)stbuf.st_rdev, (int)stbuf.st_size);
return 0;
}
/*
* This is a parsing table for argv[1]
*/
struct {
char *name;
int (*f)(struct minipc_ch *client, char **argv);
int argc;
} *cp, calls[] = {
{ "getenv", do_getenv, 3},
{ "setenv", do_setenv, 4},
{ "add", do_add, 4},
{ "strlen", do_strlen, 3},
{ "strcat", do_strcat, 4},
{ "stat", do_stat, 3},
{NULL, },
};
/*
* And this is the trivial main function -- more error checking than code
*/
int main(int argc, char **argv)
{
struct minipc_ch *client;
if (argc <= 2) {
fprintf(stderr, "%s: use \"%s <shmid> <cmd> [<arg> ...]\n",
argv[0], argv[0]);
exit(1);
}
client = minipc_client_create(argv[1], 0);
if (!client) {
fprintf(stderr, "%s: rpc_open(%s): %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
argv[1] = argv[0]; argv++; argc--;
/* scan the table */
for (cp = calls; cp->name; cp++)
if (!strcmp(argv[1], cp->name))
break;
if (!cp->name) {
fprintf(stderr, "%s: no such command \"%s\"\n", argv[0],
argv[1]);
exit(1);
}
if (argc != cp->argc) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
argv[0], argv[1]);
exit(1);
}
/* run the RPC we found */
if (cp->f(client, argv) < 0) {
fprintf(stderr, "%s: remote \"%s\": %s\n",
argv[0], argv[1], strerror(errno));
exit(1);
}
exit(0);
}
/*
* Example mini-ipc server for shmem
* (mostly copied from the pty server code)
*
* Copyright (C) 2011-2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file includes the RPC functions exported by the server.
* RPC-related stuff has been split to a different file for clarity.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "minipc.h"
#include "shmem-structs.h"
/* Run getenv and setenv on behalf of the client */
static int shm_server_do_getenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *res = getenv(envname);
strcpy(ret, res); /* FIXME: max size */
return 0;
}
static int shm_server_do_setenv(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *envname = (void *)args;
char *envval;
args = minipc_get_next_arg(args, pd->args[0]);
envval = (void *)args;
setenv(envname, envval, 1);
return 0;
}
/* add two integer numbers */
static int shm_server_do_add(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int a, b, sum;
a = args[0];
args = minipc_get_next_arg(args, pd->args[0]);
b = args[0];
sum = a + b;
*(int *)ret = sum;
return 0;
}
/* calculate strlen for a string of the client */
static int shm_server_do_strlen(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s = (void *)args;
*(int *)ret = strlen(s);
return 0;
}
/* strcat two remote strings and pass the result back */
static int shm_server_do_strcat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
char *s, *t;
char scat[256];
s = (void *)args;
args = minipc_get_next_arg(args, pd->args[0]);
t = (void *)args;
strncpy(scat, s, sizeof(scat));
strncat(scat, t, sizeof(scat) - strlen(s));
strcpy(ret, scat); /* FIXME: max size */
return 0;
}
/* stat a pathname received from the remote party */
static int shm_server_do_stat(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct stat stbuf;
char *fname = (void *)args;
if (stat(fname, &stbuf) < 0)
return -1;
memcpy(ret, &stbuf, sizeof(stbuf));
return 0;
}
/*
* The following is called by the main function, and exports stuff.
* All the rest happens through callbacks from the library
*/
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
int shm_export_functions(struct minipc_ch *ch)
{
int i;
/* Use a handy table for the registration loop */
static struct {
struct minipc_pd *desc;
minipc_f *f;
} export_list [] = {
{&rpc_getenv, shm_server_do_getenv},
{&rpc_setenv, shm_server_do_setenv},
{&rpc_add, shm_server_do_add},
{&rpc_strlen, shm_server_do_strlen},
{&rpc_strcat, shm_server_do_strcat},
{&rpc_stat, shm_server_do_stat},
};
/*
* Complete the data structures by filling the function pointers
* and then register each of the exported procedures
*/
for (i = 0; i < ARRAY_SIZE(export_list); i++) {
export_list[i].desc->f = export_list[i].f;
if (minipc_export(ch, export_list[i].desc) < 0)
return -1;
}
return 0;
}
/* the main function only runs the server */
int main(int argc, char **argv)
{
struct minipc_ch *ch;
if (argc != 2) {
fprintf(stderr, "%s: use \"%s <shmid>\n", argv[0], argv[0]);
exit(1);
}
ch = minipc_server_create(argv[1], 0);
if (!ch) {
fprintf(stderr, "%s: rpc_open(%s): %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
minipc_set_logfile(ch, stderr);
/* Register your functions */
if (shm_export_functions(ch)) {
fprintf(stderr, "%s: exporting RPC functions: %s\n", argv[0],
strerror(errno));
exit(1);
}
/* Then just obey requests */
while (1) {
minipc_server_action(ch, 1000);
}
}
/*
* Structures for example shmem mini-ipc backends
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
/*
* This file defines the data structures used to describe the RPC calls
* between server and client. Note that the function pointers are only
* instantiated in the server
*/
#include "minipc.h"
#include "shmem-structs.h"
#include <sys/stat.h>
/* run getenv in the server */
struct minipc_pd rpc_getenv = {
.name = "getenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run setenv in the server */
struct minipc_pd rpc_setenv = {
.name = "setenv",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* addition of two integers */
struct minipc_pd rpc_add = {
.name = "add",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
/* horribly run strlen and strcat */
struct minipc_pd rpc_strlen = {
.name = "strlen",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
struct minipc_pd rpc_strcat = {
.name = "strcat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
/* run "stat" on a file name */
struct minipc_pd rpc_stat = {
.name = "stat",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct stat),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
extern struct minipc_pd rpc_getenv;
extern struct minipc_pd rpc_setenv;
extern struct minipc_pd rpc_add;
extern struct minipc_pd rpc_strlen;
extern struct minipc_pd rpc_strcat;
extern struct minipc_pd rpc_stat;
/*
* Example mini-ipc client
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include "minipc.h"
#define TRIVIAL_TIMEOUT 100 /* ms */
/* The description here is the same as in the server */
const struct minipc_pd ss_sum_struct = {
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_tod_struct = {
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_sqrt_struct = {
.name = "sqrt",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *client;
int a, b, c, ret;
struct timeval tv;
double rt_in, rt_out;
client = minipc_client_create("trivial", 0);
if (!client) {
fprintf(stderr, "%s: client_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(client, stderr);
/*
* gettod, sum, sum, gettod
* pause a while in-between, so several clients can be run
* concurrently as a load test on the server
*/
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_tod_struct, &tv, NULL);
if (ret < 0) {
goto error;
}
printf("tv: %li.%06li\n", tv.tv_sec, tv.tv_usec);
usleep(500*1000);
a = 345; b = 628;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sum_struct, &c, a, b);
if (ret < 0) {
goto error;
}
printf("%i + %i = %i\n", a, b, c);
usleep(500*1000);
a = 10; b = 20;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sum_struct, &c, a, b);
if (ret < 0) {
goto error;
}
printf("%i + %i = %i\n", a, b, c);
usleep(500*1000);
rt_in = 2.0;
ret = minipc_call(client, TRIVIAL_TIMEOUT, &ss_sqrt_struct, &rt_out, rt_in);
if (ret < 0) {
goto error;
}
printf("sqrt(%lf) = %lf\n", rt_in, rt_out);
usleep(500*1000);
return 0;
error:
fprintf(stderr, "Error in rpc: %s\n", strerror(errno));
exit(1);
}
/*
* Example mini-ipc server
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released in the public domain
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <sys/time.h>
#include "minipc.h"
/* A function that ignores the RPC and is written normally */
static int ss_do_sum(int a, int b)
{
return a + b;
}
/* The following ones are RPC-aware */
static int ss_sum_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
int i;
i = ss_do_sum(args[0], args[1]);
*(int *)ret = i;
return 0;
}
static int ss_tod_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
struct timeval tv;
gettimeofday(&tv, NULL);
*(struct timeval *)ret = tv;
return 0;
}
static int ss_sqrt_function(const struct minipc_pd *pd,
uint32_t *args, void *ret)
{
double input, output;
input = *(double *)args;
output = sqrt(input);
*(double *)ret = output;
return 0;
}
/* Describe the three functions above */
const struct minipc_pd ss_sum_struct = {
.f = ss_sum_function,
.name = "sum",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_tod_struct = {
.f = ss_tod_function,
.name = "gettimeofday",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, struct timeval),
.args = {
MINIPC_ARG_END,
},
};
const struct minipc_pd ss_sqrt_struct = {
.f = ss_sqrt_function,
.name = "sqrt",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_DOUBLE, double),
MINIPC_ARG_END,
},
};
int main(int argc, char **argv)
{
struct minipc_ch *server;
server = minipc_server_create("trivial", 0);
if (!server) {
fprintf(stderr, "%s: server_create(): %s\n", argv[0],
strerror(errno));
exit(1);
}
minipc_set_logfile(server, stderr);
minipc_export(server, &ss_sum_struct);
minipc_export(server, &ss_tod_struct);
minipc_export(server, &ss_sqrt_struct);
while (1) {
if (minipc_server_action(server, 1000) < 0) {
fprintf(stderr, "%s: server_action(): %s\n", argv[0],
strerror(errno));
exit(1);
}
fprintf(stdout, "%s: looping...\n", __func__);
}
}
/*
* Mini-ipc: Exported functions for client operation
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <poll.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>
#include <sys/socket.h>
#include "minipc-int.h"
struct minipc_ch *minipc_client_create(const char *name, int f)
{
return __minipc_link_create(name, MPC_USER_FLAGS(f) | MPC_FLAG_CLIENT);
}
int minipc_call(struct minipc_ch *ch, int millisec_timeout,
const struct minipc_pd *pd, void *ret, ...)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_shmem *shm = link->memaddr;
struct pollfd pfd;
int i, narg, size, retsize, pollnr;
int atype, asize;
va_list ap;
struct mpc_req_packet *p_out, _pkt_out = {"",};
struct mpc_rep_packet *p_in, _pkt_in;
CHECK_LINK(link);
if (shm) {
p_out = &shm->request;
p_in = &shm->reply;
} else {
p_out = & _pkt_out;
p_in = & _pkt_in;
}
/* Build the packet to send out -- marshall args */
if (link->logf) {
fprintf(link->logf, "%s: calling \"%s\"\n",
__func__, pd->name);
}
memcpy(p_out->name, pd->name, MINIPC_MAX_NAME);
va_start(ap, ret);
for (i = narg = 0; ; i++) {
int next_narg = narg;
atype = MINIPC_GET_ATYPE(pd->args[i]);
asize = MINIPC_GET_ASIZE(pd->args[i]);
next_narg += MINIPC_GET_ANUM(asize);
if (next_narg >= MINIPC_MAX_ARGUMENTS) /* unlikely */
goto doesnt_fit;
switch (atype) {
case MINIPC_ATYPE_NONE:
goto out; /* end of list */
case MINIPC_ATYPE_INT:
p_out->args[narg++] = va_arg(ap, int);
break;
case MINIPC_ATYPE_INT64:
*(uint64_t *)(p_out->args + narg)
= va_arg(ap, uint64_t);
narg = next_narg;
break;
case MINIPC_ATYPE_DOUBLE:
*(double *)(p_out->args + narg) = va_arg(ap, double);
narg = next_narg;
break;
case MINIPC_ATYPE_STRING:
{
char *sin = va_arg(ap, void *);
char *sout = (void *)(p_out->args + narg);
int slen = strlen(sin);
int alen;
/*
* argument len is arbitrary, terminate and 4-align
* we can't use next_narg here, as len changes
*/
alen = MINIPC_GET_ANUM(slen + 1);
if (narg + alen >= MINIPC_MAX_ARGUMENTS)
goto doesnt_fit;
strcpy(sout, sin);
narg += alen;
break;
}
case MINIPC_ATYPE_STRUCT:
memcpy(p_out->args + narg, va_arg(ap, void *), asize);
narg = next_narg;
break;
default:
if (link->logf) {
fprintf(link->logf, "%s: unkown type 0x%x\n",
__func__, atype);
}
errno = EPROTO;
return -1;
}
}
out:
va_end(ap);
if (!shm)
{
size = sizeof(p_out->name) + sizeof(p_out->args[0]) * narg;
if (send(ch->fd, p_out, size, 0) < 0) {
/* errno already set */
return -1;
}
}
if (shm) {
if ( mpc_mem_request(ch, millisec_timeout ) < 0 ) // timeout or other shit
return -1;
size = retsize = sizeof(shm->reply);
} else {
/* Wait for the reply packet */
pfd.fd = ch->fd;
pfd.events = POLLIN | POLLHUP;
pfd.revents = 0;
pollnr = poll(&pfd, 1, millisec_timeout);
if (pollnr < 0) {
/* errno already set */
return -1;
}
if (pollnr == 0) {
errno = ETIMEDOUT;
return -1;
}
/* this "size" is wrong for strings, so recv the max size */
size = MINIPC_GET_ASIZE(pd->retval) + sizeof(uint32_t);
retsize = recv(ch->fd, p_in, sizeof(*p_in), 0);
}
/* if very short, we have a problem */
if (retsize < (sizeof(p_in->type)) + sizeof(int))
goto too_short;
/* remote error reported */
if (MINIPC_GET_ATYPE(p_in->type) == MINIPC_ATYPE_ERROR) {
int remoteerr = *(int *)&p_in->val;
if (link->logf) {
fprintf(link->logf, "%s: remote error \"%s\"\n",
__func__, strerror(remoteerr));
}
*(int *)ret = remoteerr;
errno = EREMOTEIO;
return -1;
}
/* another check: the return type must match */
if (MINIPC_GET_ATYPE(p_in->type) != MINIPC_GET_ATYPE(pd->retval)) {
if (link->logf) {
fprintf(link->logf, "%s: wrong code %08x (not %08x)\n",
__func__, p_in->type, pd->retval);
}
errno = EPROTO;
return -1;
}
/* check size */
if (retsize < size)
goto too_short;
/* all good */
memcpy(ret, &p_in->val, MINIPC_GET_ASIZE(p_in->type));
return 0;
too_short:
if (link->logf) {
fprintf(link->logf, "%s: short reply (%i bytes)\n",
__func__, retsize);
}
errno = EPROTO;
return -1;
doesnt_fit:
if (link->logf) {
fprintf(link->logf, "%s: rpc call \"%s\" won't fit %i slots\n",
__func__, pd->name, MINIPC_MAX_ARGUMENTS);
}
errno = EPROTO;
return -1;
}
/*
* Mini-ipc: Core library functions and data
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
* Based on ideas by Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* Update from Tom: these are not my ideas. This is fucking stupid beardy '70s UNIX code
* because its' author didn't like assembly.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include "minipc-int.h"
struct mpc_link *__mpc_base;
static int __mpc_poll_usec = MINIPC_DEFAULT_POLL;
void mpc_free_flist(struct mpc_link *link, struct mpc_flist *flist)
{
struct mpc_flist **nextp;
/* Look for flist and release it*/
for (nextp = &link->flist; (*nextp); nextp = &(*nextp)->next)
if (*nextp == flist)
break;
if (!*nextp) {
if (link->logf)
fprintf(link->logf, "%s: function not found %p (%s)\n",
__func__, flist, flist->pd->name);
return;
}
*nextp = flist->next;
if (link->logf)
fprintf(link->logf, "%s: unexported function %p (%s)\n",
__func__, flist->pd->f, flist->pd->name);
free(flist);
}
int minipc_close(struct minipc_ch *ch)
{
struct mpc_link *link = mpc_get_link(ch);
struct mpc_link **nextp;
CHECK_LINK(link);
/* Look for link in our list */
for (nextp = &__mpc_base; (*nextp); nextp = &(*nextp)->nextl)
if (*nextp == link)
break;
if (!*nextp) {
errno = ENOENT;
return -1;
}
(*nextp)->nextl = link->nextl;
if (link->logf) {
fprintf(link->logf, "%s: found link %p (fd %i)\n",
__func__, link, link->ch.fd);
}
if(ch->fd > 0)
close(ch->fd);
if (link->flags & MPC_FLAG_SHMEM)
shmdt(link->memaddr);
if (link->flags & MPC_FLAG_DEVMEM)
munmap(link->memaddr, link->memsize);
/* Release allocated functions */
while (link->flist)
mpc_free_flist(link, link->flist);
free(link);
return 0;
}
int minipc_set_poll(int usec)
{
int ret = __mpc_poll_usec;
if (usec <= 0) {
errno = EINVAL;
return -1;
}
__mpc_poll_usec = usec;
return ret;
}
int minipc_set_logfile(struct minipc_ch *ch, FILE *logf)
{
struct mpc_link *link = mpc_get_link(ch);
CHECK_LINK(link);
link->logf = logf;
return 0;
}
/* helper function for memory-based channels */
static struct mpc_link *__minipc_memlink_create(struct mpc_link *link)
{
void *addr = 0;
long offset;
int memsize, ret;
int pagesize = getpagesize();
memsize = (sizeof(struct mpc_shmem) + pagesize - 1) & ~pagesize;
/* Warning: no check for trailing garbage in name */
if (sscanf(link->name, "shm:%li", &offset)) {
ret = shmget(offset, memsize, IPC_CREAT | 0666);
if (ret < 0)
return NULL;
addr = shmat(ret, NULL, SHM_RND);
if (addr == (void *)-1)
return NULL;
link->flags |= MPC_FLAG_SHMEM;
}
/* Warning: no check for trailing garbage in name -- hex mandatory */
if (sscanf(link->name, "mem:%lx", &offset)) {
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0)
return NULL;
addr = mmap(0, memsize, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, offset);
close(fd);
if (addr == (MAP_FAILED))
return NULL;
link->flags |= MPC_FLAG_DEVMEM;
}
link->memaddr = addr;
link->memsize = memsize;
if (link->flags & MPC_FLAG_SERVER)
memset(addr, 0, sizeof(struct mpc_shmem));
return link;
err_unmap:
if (link->flags & MPC_FLAG_SHMEM)
shmdt(link->memaddr);
if (link->flags & MPC_FLAG_DEVMEM)
munmap(link->memaddr, link->memsize);
return NULL;
}
/* create a link, either server or client */
struct minipc_ch *__minipc_link_create(const char *name, int flags)
{
struct mpc_link *link;
struct sockaddr_un sun;
int fd, i;
link = calloc(1, sizeof(*link));
if (!link) return NULL;
link->magic = MPC_MAGIC;
link->flags = flags;
link->last_request_id = 0;
strncpy(link->name, name, sizeof(link->name) -1);
/* special-case the memory-based channels */
if (!strncmp(name, "shm:", 4) || !strncmp(name, "mem:", 4)) {
if (!__minipc_memlink_create(link))
goto out_free;
goto out_success;
}
/* now create the socket and prepare the service */
fd = socket(SOCK_STREAM, AF_UNIX, 0);
if(fd < 0)
goto out_free;
link->ch.fd = fd;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, MINIPC_BASE_PATH);
strcat(sun.sun_path, "/");
strcat(sun.sun_path, link->name);
mkdir(MINIPC_BASE_PATH, 0777); /* may exist, ignore errors */
if (flags & MPC_FLAG_SERVER) {
unlink(sun.sun_path);
if (bind (fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
goto out_close;
if (listen(fd, 5) < 0)
goto out_close;
} else { /* client */
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) < 0)
goto out_close;
}
/* success: fix your fd values, link to the list and return */
out_success:
if (flags & MPC_FLAG_SERVER) {
for (i = 0; i < MINIPC_MAX_CLIENTS; i++)
link->fd[i] = -1;
FD_ZERO(&link->fdset);
FD_SET(link->ch.fd, &link->fdset);
}
link->addr = sun;
link->nextl = __mpc_base;
__mpc_base = link;
return &link->ch;
out_close:
close(fd);
out_free:
free(link);
return NULL;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Subproject commit ec89448c1db94019a719c68329297f90edd210de
Subproject commit ca2dd2135ec627b164ff6bfc5dd33b5a22c01be3
......@@ -377,6 +377,7 @@ declare -A inst_dotc_ppsi_key_mapping='(\
[PROFILE_PTP]="profile ptp" [PROFILE_WR]="profile wr" [PROFILE_HA]="profile ha" [PROFILE_CUSTOM]="profile custom" \
[DESIRADE_STATE_MASTER]="desiredState master" [DESIRADE_STATE_SLAVE]="desiredState slave" [DESIRADE_STATE_PASSIVE]="desiredState passive" \
[ANNOUNCE_INTERVAL]="logAnnounceInterval" [ANNOUNCE_RECEIPT_TIMEOUT]="announceReceiptTimeout" \
[SYNC_INTERVAL]="logSyncInterval" \
[MIN_DELAY_REQ_INTERVAL]="logMinDelayReqInterval" [MIN_PDELAY_REQ_INTERVAL]="logMinPDelayReqInterval" \
[ASYMMETRY_CORRECTION_ENABLE]="asymmetryCorrectionEnable" \
[BMODE_MASTER_ONLY]="masterOnly" \
......@@ -426,14 +427,6 @@ if [ -n "$CONFIG_PTP_OPT_DOMAIN_NUMBER" ]; then
globals[domain-number]="$CONFIG_PTP_OPT_DOMAIN_NUMBER"
fi
if [ -n "$CONFIG_PTP_OPT_ANNOUNCE_INTERVAL" ]; then
globals[announce-interval]="$CONFIG_PTP_OPT_ANNOUNCE_INTERVAL"
fi
if [ -n "$CONFIG_PTP_OPT_SYNC_INTERVAL" ]; then
globals[sync-interval]="$CONFIG_PTP_OPT_SYNC_INTERVAL"
fi
if [ -n "$CONFIG_PTP_OPT_PRIORITY1" ]; then
globals[priority1]="$CONFIG_PTP_OPT_PRIORITY1"
fi
......
This diff is collapsed.
This diff is collapsed.
-include ../Makefile.specific
# We are now Kconfig-based
-include ../../.config
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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