Commit 9aee73bf authored by Adam Wujek's avatar Adam Wujek 💬

Merge branch 'adam-snmp2'

Please not that some commits had non backward compatible changes in MIB.
However, end result of MIB is backward compatible.
parents f0bdc562 a08d32c7
......@@ -70,44 +70,6 @@ config REMOTE_SYSLOG_UDP
help
Select UDP to send system logs. If not set, TCP is used.
config SNMP_TRAPSINK_ADDRESS
string "Static IP address or name where to send SNMPv1 traps"
help
If not empty, the address or name is ussed as "trapsink"
destination in the SNMP configuration file for the switch.
It empty, no v1 traps are generated. If both this and
the following TRAP2SINK_ADDRESS are set, snmpd sends two
traps (one per protocol version). Trapcommunity is "public"
(not configurable so far).
config SNMP_TRAP2SINK_ADDRESS
string "Static IP address or name where to send SNMPv2c traps"
help
If not empty, the address or name is ussed as "trap2sink"
destination in the SNMP configuration file for the switch.
It empty, no v2c traps are generated. Trapcommunity is "public"
(not configurable so far).
config SNMP_RO_COMMUNITY
string "Read-only community name for SNMP V1/V2 management"
default "public"
help
If not empty, the 'community' name is a sort of password,
that travels as clear text (we don't support encrypted SNMPv3
yet). The default is good for testing, but should be changed
for production. Please note, however, that the switch processes
SNMP only on the CPU Ethernet port (the copper "management" port).
config SNMP_RW_COMMUNITY
string "Read-write community name for SNMP V1/V2 management"
default "private"
help
If not empty, the 'community' name is a sort of password,
that travels as clear text (we don't support encrypted SNMPv3
yet). The default is good for testing, but should be changed
for production. Please note, however, that the switch processes
SNMP only on the CPU Ethernet port (the copper "management" port).
config WRS_LOG_HAL
string "Logging directions for the WR hal"
default "daemon.info"
......@@ -231,7 +193,7 @@ menu "SFP and Media Timing Configuration"
config SFP00_PARAMS
string "Parameters for one SFP device type"
default "name=AXGE-1254-0531,tx=10,rx=10,wl_txrx=1310+1490"
default "vn=Axcen Photonics,pn=AXGE-1254-0531,tx=10,rx=10,wl_txrx=1310+1490"
help
This parameter, and the following ones, are used to
configure the timing parameters of a specific SFP
......@@ -242,7 +204,7 @@ config SFP00_PARAMS
config SFP01_PARAMS
string "Parameters for one SFP device type"
default "name=AXGE-3454-0531,tx=10,rx=10,wl_txrx=1490+1310"
default "vn=Axcen Photonics,pn=AXGE-3454-0531,tx=10,rx=10,wl_txrx=1490+1310"
config SFP02_PARAMS
string "Parameters for one SFP device type"
......@@ -361,8 +323,76 @@ config PTP_CUSTOM
endchoice
menu "Management configuration"
config SNMP_TRAPSINK_ADDRESS
string "Static IP address or name where to send SNMPv1 traps"
help
If not empty, the address or name is ussed as "trapsink"
destination in the SNMP configuration file for the switch.
It empty, no v1 traps are generated. If both this and
the following TRAP2SINK_ADDRESS are set, snmpd sends two
traps (one per protocol version). Trapcommunity is "public"
(not configurable so far).
config SNMP_TRAP2SINK_ADDRESS
string "Static IP address or name where to send SNMPv2c traps"
help
If not empty, the address or name is ussed as "trap2sink"
destination in the SNMP configuration file for the switch.
It empty, no v2c traps are generated. Trapcommunity is "public"
(not configurable so far).
config SNMP_RO_COMMUNITY
string "Read-only community name for SNMP V1/V2 management"
default "public"
help
If not empty, the 'community' name is a sort of password,
that travels as clear text (we don't support encrypted SNMPv3
yet). The default is good for testing, but should be changed
for production. Please note, however, that the switch processes
SNMP only on the CPU Ethernet port (the copper "management" port).
config SNMP_RW_COMMUNITY
string "Read-write community name for SNMP V1/V2 management"
default "private"
help
If not empty, the 'community' name is a sort of password,
that travels as clear text (we don't support encrypted SNMPv3
yet). The default is good for testing, but should be changed
for production. Please note, however, that the switch processes
SNMP only on the CPU Ethernet port (the copper "management" port).
config SNMP_TEMP_THOLD_FPGA
int "Threshold level for FPGA temperature"
default "0"
help
Threshold level for FPGA temperature, when exceeded warning is
notified by WR-SWITCH-MIB::tempWarning
config SNMP_TEMP_THOLD_PLL
int "Threshold level for PLL temperature"
default "0"
help
Threshold level for PLL temperature, when exceeded warning is
notified by WR-SWITCH-MIB::tempWarning
config SNMP_TEMP_THOLD_PSL
int "Threshold level for Power Supply Left (PSL) temperature"
default "0"
help
Threshold level for Power Supply Left (PSL) temperature, when
exceeded warning is notified by WR-SWITCH-MIB::tempWarning
config SNMP_TEMP_THOLD_PSR
int "Threshold level for Power Supply Right (PSR) temperature"
default "0"
help
Threshold level for Power Supply Right (PSR) temperature, when
exceeded warning is notified by WR-SWITCH-MIB::tempWarning
endmenu
config PTP_CUSTOM_FILENAME
string "Pathname for your custom ppsi.conf"
depends on PTP_CUSTOM
default "/wr/etc/ppsi-custom.conf"
......@@ -564,12 +564,6 @@ but sooner or later I'll fix it. As a side effect, I now use
the two contexts concurrently in an ambiguous way. No, I'm not
proud of this code.
I don't feel confident with all the data structures as yet, and there
still is some magic in all of this. This is confirmed by a buglet in
the current code, that makes @i{snmpwalk} always return one item after
the end of the table -- most likely I need to fix @t{next_entry()}
to return @t{NULL} earlier.
@c =========================================================================
@node wrsPpsi
@section wrsPpsi
......
......@@ -1490,10 +1490,8 @@ RPC servers are created in the following places:
@table @code
@item userspace/wrsw_rtud/rtud_exports.c
@c FIXME: rtud should use shmem for status
The socket is called @t{rtud} and is used by the @i{rtu_stat}
program to gather runtime information, and by @i{wrs_vlans}
The socket is called @t{rtud} and is used by @i{wrs_vlans}
to request actual actions.
@item userspace/ppsi/arch-wrs/wrs-startup.c
......@@ -1507,9 +1505,8 @@ RPC servers are created in the following places:
This channel is called @t{wrsw_hal} but the code uses is through
the macro @t{WRSW_HAL_SERVER_ADDR}. The RPC server is used
both for status and commands, but status is already exported
in shared memory, and we are converting the clients; status
RPC calls will be removed soon.
only for commands. All clients use shared memory to get status
information.
@item wrpc-sw::ipc/rt_ipc.c
......@@ -1522,28 +1519,17 @@ Clients are created in the following places:
@table @code
@item userspace/tools/rtu_stat.c
@c FIXME: rtu_stat should use shmem
The tool connects to @i{rtud} to get runtime information.
@item userspace/tools/wrs_vlans.c
@t{wrs_vlans} connects to the @i{rtud} to request configuration
actions related to vlan setup.
@item userspace/wrsw_hal/hal_exports.c
@c FIXME: don't check with a socket, but with the shmem mechanism
A temporary client is created to check whether a HAL process
is already running.
actions related to vlan setup. All status information is passed
through shared memory.
@item userspace/tools/wr_mon.c
@c FIXME: wr_mon should use shmem
The tty-based monitoring interface connects to @i{ptpd} (@i{ppsi})
to get run-time information. It is going to be moved to
shared memory.
to enable or disable tracking. All status information is passed
from @i{HAL} and @i{ppsi} through shared memory.
@item userspace/libwr/hal_client.c
......@@ -1600,13 +1586,6 @@ the processes (excluding the @sc{rt} subsystem).
Called by library code (@t{halexp_lock_cmd}) but not used.
Called by @i{ppsi} directly in @t{wrs-time.c}.
@item rtud::get_fd_list
@itemx rtud::get_vd_list
@c FIXME: shmem these
Called by @i{rtu_stat} to report information. They can be
moved to shared memory.
@item rtud::clear_entries
@itemx rtud::add_entry
......
......@@ -217,13 +217,6 @@ interface with gateware is now simpler than it was, but software still
has remnants of old data structure. Also, the multi-threaded approach
is overkill, and the program could benefit from a simplification.
@item The SFP code needs to be cleaned up. There is unsafe string
management in 16-wide non-terminated fields, and we don't really
report all the information we have (we should copy all SFP
information to shmem: we are almost there but not completely).
Moreover, using the device name without the vendor
name is unsafe: some vendors use generic device names.
@item The @t{TRACE} support it not really working. We have serious
warning messages that were never reported. We should really print
everything to stderr and let syslog manage it (according to user
......
......@@ -464,6 +464,15 @@ value is changed by the web interface, proper action is taken.
default. Community values are strings and they default to
@t{public} and @t{private}.
@item CONFIG_SNMP_TEMP_THOLD_FPGA
@itemx CONFIG_SNMP_TEMP_THOLD_PLL
@itemx CONFIG_SNMP_TEMP_THOLD_PSL
@itemx CONFIG_SNMP_TEMP_THOLD_PSR
Threshold levels for FPGA, PLL, Power Supply Left (PSL) and Power
Supply Right (PSR) temperature sensors. When any temperature exceeds
threshold level SNMP object @t{WR-SWITCH-MIB::tempWarning} will change
accordingly.
@item CONFIG_WRS_LOG_HAL
@itemx CONFIG_WRS_LOG_RTU
@itemx CONFIG_WRS_LOG_PTP
......@@ -553,7 +562,7 @@ This is, for explanation's sake, an example of such items:
@smallexample
CONFIG_PORT09_PARAMS="name=wr9,tx=226214,rx=226758,role=slave,fiber=2"
CONFIG_SFP00_PARAMS="name=AXGE-1254-0531,tx=0,rx=0,wl_txrx=1310+1490"
CONFIG_SFP00_PARAMS="pn=AXGE-1254-0531,tx=0,rx=0,wl_txrx=1310+1490"
CONFIG_FIBER02_PARAMS="alpha_1310_1490=2.6787e-04"
@end smallexample
......@@ -589,6 +598,35 @@ wavelength, and is determined, again, by calibration.
Please note that only one alpha value is provided, because the
opposite one (@t{alpha_1490_1310}) is calculated by software.
@subheading SFP name matching
SFP matching is based on vendor name (@i{vn}), part number (@i{pn}) and vendor
serial (@i{vs}). While matching SFP's values are compared with values stored in
@t{CONFIG_SFPXX_PARAMS}.
First try is to match all SFP identifiers (@i{vn}, @i{pn} and @i{vs}) with
stored in config. If match is not successful, @i{vn} and @i{pn} of SFP are
compared only with config entries without vendor serial. If match is still not
found SFP's values are compared with config entries, which has defined only
part number. Such approach prevents matching SFPs to config entires with
defined serial.
Below are shown matching examples:
@smallexample
CONFIG_SFP00_PARAMS="vn=Axcen Photonics,pn=AXGE-3454-0531,vs=AX12390009629,tx=0,rx=0,..."
@end smallexample
Above config may be matched only to one SFP.
@smallexample
CONFIG_SFP01_PARAMS="vn=Axcen Photonics,pn=AXGE-3454-0531,tx=0,rx=0,wl_txrx=1310+1490"
@end smallexample
Above config may be matched only to SFP with vendor name "Axcen Photonics" and
part number "AXGE-3454-0531", with exception to these SFPs that were matched to
example like above (with vendor serial defined).
@smallexample
CONFIG_SFP02_PARAMS="pn=AXGE-3454-0531,tx=0,rx=0,wl_txrx=1310+1490"
@end smallexample
Config above will be matched to all SFPs with part number "AXGE-3454-0531",
that were not matched by configs above.
@subheading Other Deployments
The example above matches the choices we make at CERN, where our
......@@ -1232,10 +1270,6 @@ This a summary of the available tables and scalars:
@end table
@b{Note:} due to a buglet of mine, there is an extra item at
the end of each table (@t{96.100.2} and @t{96.100.3.2}. It makes no
harm, so its removal is not high priority.
@c @b{Note:} due to a bug in management of 64-bit values in @i{net-snmp},
@c we are using a bad work-around in the code, that may cause wrong values
@c to be returned by other versions of the agent, where this bug is fixed.
......@@ -1296,6 +1330,14 @@ WR-SWITCH-MIB::wrsDateTAI.0 = Counter64: 1406623390
WR-SWITCH-MIB::wrsDateString.0 = STRING: 2014-07-29-08:43:10
@end smallexample
Another example is to print all objects exported by switch.
@smallexample
snmpwalk -c public -v 2c wrs -m all \
-M ${WRS_OUTPUT_DIR}/build/buildroot-2011.11/output/build/netsnmp-5.6.1.1/mibs/\
:${WR_SWITCH_SW}/userspace/snmpd/ \
1
@end smallexample
@c ==========================================================================
@node show-pstats
@section show-pstats
......
......@@ -86,46 +86,12 @@ typedef struct {
*/
/* Prototypes of functions that call on rpc */
extern int halexp_check_running(void);
extern int halexp_reset_port(const char *port_name);
extern int halexp_calibration_cmd(const char *port_name, int command, int on_off);
extern int halexp_lock_cmd(const char *port_name, int command, int priority);
extern int halexp_pps_cmd(int cmd, hexp_pps_params_t *params);
/* Export structures, shared by server and client for argument matching */
#ifdef HAL_EXPORT_STRUCTURES
//int halexp_check_running();
struct minipc_pd __rpcdef_check_running = {
.name = "check_running",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_END,
},
};
//int halexp_reset_port(const char *port_name);
struct minipc_pd __rpcdef_reset_port = {
.name = "reset_port",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_END,
},
};
//int halexp_calibration_cmd(const char *port_name, int command, int on_off);
struct minipc_pd __rpcdef_calibration_cmd = {
.name = "calibration_cmd",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRING, char *),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
//int halexp_lock_cmd(const char *port_name, int command, int priority);
struct minipc_pd __rpcdef_lock_cmd = {
.name = "lock_cmd",
......
......@@ -4,43 +4,14 @@
#include <stdio.h>
#include <stdlib.h>
typedef struct{
int valid;
char slave_servo_state[32];
char sync_source[32];
int tracking_enabled;
int64_t mu;
int64_t delay_ms;
int64_t delta_tx_m;
int64_t delta_rx_m;
int64_t delta_tx_s;
int64_t delta_rx_s;
int64_t fiber_asymmetry;
int64_t total_asymmetry;
int64_t cur_offset;
int64_t cur_setpoint;
int64_t cur_skew;
int64_t update_count;
} ptpdexp_sync_state_t ;
#define PTPDEXP_COMMAND_TRACKING 1
#define PTPDEXP_COMMAND_MAN_ADJUST_PHASE 2
extern int ptpdexp_get_sync_state(ptpdexp_sync_state_t *state);
extern int ptpdexp_cmd(int cmd, int value);
/* Export structures, shared by server and client for argument matching */
#ifdef PTP_EXPORT_STRUCTURES
//int ptpdexp_get_sync_state(ptpdexp_sync_state_t *state);
struct minipc_pd __rpcdef_get_sync_state = {
.name = "get_sync_state",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, ptpdexp_sync_state_t),
.args = {
MINIPC_ARG_END,
},
};
//int ptpdexp_cmd(int cmd, int value);
struct minipc_pd __rpcdef_cmd = {
.name = "cmd",
......
......@@ -28,6 +28,7 @@
#include <libwr/pio.h>
#include <libwr/fan.h>
#include <libwr/hal_shmem.h>
#include <at91_softpwm.h>
......@@ -39,7 +40,10 @@
#include "spwm-regs.h"
#include <libwr/util.h>
#define FAN_TEMP_SENSOR_ADDR 0x4c
#define TEMP_SENSOR_ADDR_FPGA 0x4A /* (7bits addr) IC19 Below FPGA */
#define TEMP_SENSOR_ADDR_PLL 0x4C /* (7bits addr) IC18 PLLs */
#define TEMP_SENSOR_ADDR_PSL 0x49 /* (7bits addr) IC20 Power supply left */
#define TEMP_SENSOR_ADDR_PSR 0x4D /* (7bits addr) IC17 Power supply right */
#define DESIRED_TEMPERATURE 55.0
......@@ -204,7 +208,7 @@ static int shw_init_i2c_sensors()
return 0;
}
int shw_init_fans()
int shw_init_fans(void)
{
uint32_t val = 0;
......@@ -250,7 +254,11 @@ int shw_init_fans()
shw_init_i2c_sensors();
tmp100_write_reg(FAN_TEMP_SENSOR_ADDR, 1, 0x60); // 12-bit resolution
/* set all to 12-bit resolution */
tmp100_write_reg(TEMP_SENSOR_ADDR_FPGA, 1, 0x60);
tmp100_write_reg(TEMP_SENSOR_ADDR_PLL, 1, 0x60);
tmp100_write_reg(TEMP_SENSOR_ADDR_PSL, 1, 0x60);
tmp100_write_reg(TEMP_SENSOR_ADDR_PSR, 1, 0x60);
pi_init(&fan_pi);
......@@ -263,17 +271,24 @@ int shw_init_fans()
* Reads out the temperature and drives the fan accordingly
* note: This call is done by hal_main.c:hal_update()
*/
void shw_update_fans()
void shw_update_fans(struct hal_temp_sensors *sensors)
{
static int64_t last_tics = -1;
int64_t cur_tics = shw_get_tics();
if (fan_update_timeout > 0
&& (last_tics < 0 || (cur_tics - last_tics) > fan_update_timeout)) {
float t_cur = tmp100_read_temp(FAN_TEMP_SENSOR_ADDR);
/* drive fan based on PLL temperature */
float t_cur = tmp100_read_temp(TEMP_SENSOR_ADDR_PLL);
float drive = pi_update(&fan_pi, t_cur - DESIRED_TEMPERATURE);
//pr_info("t=%f,pwm=%f\n",t_cur , drive);
shw_pwm_speed(0xFF, drive / 1000); //enable two and one
/* update sensor values */
sensors->fpga = tmp100_read_reg(TEMP_SENSOR_ADDR_FPGA, 0, 2);
sensors->pll = tmp100_read_reg(TEMP_SENSOR_ADDR_PLL, 0, 2);
sensors->psl = tmp100_read_reg(TEMP_SENSOR_ADDR_PSL, 0, 2);
sensors->psr = tmp100_read_reg(TEMP_SENSOR_ADDR_PSR, 0, 2);
last_tics = cur_tics;
}
}
......@@ -6,15 +6,10 @@
#define HAL_EXPORT_STRUCTURES
#include <hal/hal_exports.h>
#include <libwr/shmem.h>
#include <libwr/hal_shmem.h>
#define DEFAULT_TO 200000 /* ms */
static struct minipc_ch *hal_ch;
static struct wrs_shm_head *hal_head;
static struct hal_port_state *hal_ports;
static int hal_nports;
int halexp_lock_cmd(const char *port_name, int command, int priority)
{
......@@ -39,17 +34,6 @@ int halexp_pps_cmd(int cmd, hexp_pps_params_t * params)
/* Some clients call this, some call the client_init() defined later */
int halexp_client_try_connect(int retries, int timeout)
{
struct hal_shmem_header *h;
struct hal_port_state *p;
hal_head = wrs_shm_get(wrs_shm_hal,"", WRS_SHM_READ);
if (!hal_head)
return -1;
h = (void *)hal_head + hal_head->data_off;
hal_nports = h->nports;
p = wrs_shm_follow(hal_head, h->ports);
hal_ports = p; /* This is used in later calls */
for (;;) {
hal_ch =
minipc_client_create(WRSW_HAL_SERVER_ADDR,
......@@ -67,8 +51,3 @@ int halexp_client_try_connect(int retries, int timeout)
return -1;
}
int halexp_client_init()
{
return halexp_client_try_connect(0, 0);
}
#ifndef I2C_SFP_H
#define I2C_SFP_H
#ifndef I2C_IO_H
#define I2C_IO_H
#include "i2c.h"
......@@ -25,4 +25,4 @@ int shw_i2c_io_scan(uint8_t * dev_map);
int shw_get_hw_ver();
uint8_t shw_get_fpga_type();
#endif //I2C_SFP_H
#endif //I2C_IO_H
......@@ -520,7 +520,7 @@ int shw_sfp_read_header(int num, struct shw_sfp_header *head)
ret = shw_sfp_module_scan();
if (!(ret & (1 << num)))
return -1;
return -2;
ret =
shw_sfp_read(num, I2C_SFP_ADDRESS, 0x0,
......@@ -562,14 +562,28 @@ int shw_sfp_read_db(void)
int error, val, index;
for (index = 0; ; index++) {
error = libwr_cfg_convert2("SFP%02i_PARAMS", "name",
error = libwr_cfg_convert2("SFP%02i_PARAMS", "pn",
LIBWR_STRING, s, index);
if (error)
return 0; /* no more, no error */
sfp = calloc(1, sizeof(*sfp));
strncpy(sfp->part_num, s, sizeof(sfp->part_num));
error = libwr_cfg_convert2("SFP%02i_PARAMS", "vn",
LIBWR_STRING, s, index);
/* copy vendor name if found */
if (!error)
strncpy(sfp->vendor_name, s, sizeof(sfp->vendor_name));
sfp->vendor_serial[0] = 0;
error = libwr_cfg_convert2("SFP%02i_PARAMS", "vs",
LIBWR_STRING, s, index);
/* copy serial name if found */
if (!error)
strncpy(sfp->vendor_serial, s,
sizeof(sfp->vendor_serial));
sfp->flags = SFP_FLAG_CLASS_DATA; /* never used */
/* These are uint32_t as I write this. So use "int val" */
......@@ -605,45 +619,60 @@ int shw_sfp_read_db(void)
return 0;
}
struct shw_sfp_caldata *shw_sfp_get_cal_data(int num)
struct shw_sfp_caldata *shw_sfp_get_cal_data(int num,
struct shw_sfp_header *head)
{
uint32_t ret;
struct shw_sfp_header head;
struct shw_sfp_caldata *t;
struct shw_sfp_caldata *other = NULL;
struct shw_sfp_caldata *match_pn_vn = NULL;
struct shw_sfp_caldata *match_pn = NULL;
char *vn = (char *)head->vendor_name;
char *pn = (char *)head->vendor_pn;
char *vs = (char *)head->vendor_serial;
int i;
ret = shw_sfp_module_scan();
if (!(ret & (1 << num))) {
printf("sfp not inserted into slot: %d\n", num);
return NULL;
}
if (shw_sfp_read_header(num, &head) < 0) {
printf("failed to read sfp header for slot %d\n", num);
return NULL;
/* Replace spaces at the end of strings with 0 needed for
* string comparison inside shw_sfp_get_cal_data.
* String may contain spaces, standard says only about space padding */
for (i = 15; i >= 0 ; i--) {
if (vn[i] != 0x20)
break;
vn[i] = 0;
}
char *pn = (char *)head.vendor_pn;
char *vs = (char *)head.vendor_serial;
int i;
for (i = 0; i < 16; i++) {
if (pn[i] == 0x20)
for (i = 15; i >= 0 ; i--) {
if (pn[i] != 0x20)
break;
pn[i] = 0;
if (vs[i] == 0x20)
}
for (i = 15; i >= 0 ; i--) {
if (vs[i] != 0x20)
break;
vs[i] = 0;
}
t = shw_sfp_cal_list;
/* In the first pass, look for serial number */
while (t) {
// printf("search1 %s %s\n", t->part_num, t->vendor_serial);
if (strncmp(pn, t->part_num, 16) == 0
&& strncmp(t->vendor_serial, "", 16) == 0)
other = t;
else if (strncmp(pn, t->part_num, 16) == 0
if (t->vendor_name[0] == 0
&& strncmp(pn, t->part_num, 16) == 0
&& t->vendor_serial[0] == 0)
/* matched pn, but vn and vs not defined */
match_pn = t;
else if (strncmp(vn, t->vendor_name, 16) == 0
&& strncmp(pn, t->part_num, 16) == 0
&& t->vendor_serial[0] == 0)
/* matched vn, pn, but vs not defined */
match_pn_vn = t;
else if (strncmp(vn, t->vendor_name, 16) == 0
&& strncmp(pn, t->part_num, 16) == 0
&& strncmp(vs, t->vendor_serial, 16) == 0)
/* matched vn, pn, vs */
return t;
t = t->next;
}
if (other)
return other;
if (match_pn_vn)
return match_pn_vn;
if (match_pn)
return match_pn;
return NULL;
}
#ifndef __LIBWR_FAN_H
#define __LIBWR_FAN_H
#include <libwr/hal_shmem.h>
#define SHW_FAN_UPDATETO_DEFAULT 5
int shw_init_fans();
void shw_update_fans();
int shw_init_fans(void);
void shw_update_fans(struct hal_temp_sensors *sensors);
#endif /* __LIBWR_FAN_H */
......@@ -4,7 +4,6 @@
#include <hal/hal_exports.h>
int halexp_client_init();
int halexp_client_try_connect(int retries, int timeout);
#endif /* __LIBWR_HAL_CLIENT_H */
......@@ -102,12 +102,24 @@ struct hal_port_state {
uint32_t ep_base;
};
struct hal_temp_sensors {
int fpga; /* IC19 */
int pll; /* IC18 */
int psl; /* IC20 Power Supply Left (PSL) */
int psr; /* IC17 Power Supply Right (PSR) */
int fpga_thold; /* Threshold value for FPGA temperature */
int pll_thold; /* Threshold value for PLL temperature */
int psl_thold; /* Threshold value for PSL temperature */
int psr_thold; /* Threshold value for PSR temperature */
};
/* This is the overall structure stored in shared memory */
#define HAL_SHMEM_VERSION 4 /* Version 4 because of new fields in struct
* hal_port_state */
#define HAL_SHMEM_VERSION 6 /* Version 6 because of new structure
* hal_temp_sensors in hal_shmem_header */
struct hal_shmem_header {
int nports;
struct hal_port_state *ports;
struct hal_temp_sensors temp;
};
static inline int state_up(int state)
......
......@@ -20,14 +20,23 @@
#define SFP_FLAG_CLASS_DATA (1 << 0)
#define SFP_FLAG_DEVICE_DATA (1 << 1)
#define SFP_FLAG_1GbE (1 << 2) /* SFP is 1GbE */
#define SFP_FLAG_IN_DB (1 << 3) /* SFP is present in data base */
#define SFP_SPEED_1Gb 0x0D /* Speed of SFP in 100MB/s. According to
* SFF-8472.PDF: By convention 1.25 Gb/s
* should be rounded up to 0Dh (13 in
* units of 100 MBd) for Ethernet
* 1000BASE-X. */
struct shw_sfp_caldata {
int flags;
uint32_t flags;
/*
* Part number used to identify it. Serial number because we
* may specify per-specimen delays, but it is not used at this
* point in time
*/
char vendor_name[16];
char part_num[16];
char vendor_serial[16];
/* Callibration data */
......@@ -103,7 +112,8 @@ int shw_sfp_read_db(void);
int shw_sfp_read_verify_header(int num, struct shw_sfp_header *head);
/* return NULL if no data found */
struct shw_sfp_caldata *shw_sfp_get_cal_data(int num);
struct shw_sfp_caldata *shw_sfp_get_cal_data(int num,
struct shw_sfp_header *head);
/* Read and verify the header all at once. returns -1 on failure */
int shw_sfp_read_verify_header(int num, struct shw_sfp_header *head);
......
ppsi @ ea68c737
Subproject commit 2181324ae355f72299abc63b5fa8c9e1fcc742b1
Subproject commit ea68c737c6049f6fbe75827f3be236956c2207bc
......@@ -20,7 +20,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump
# defer running "net-snmp-config --cflags" so it is visible in make output
CFLAGS += -fPIC -Wall $$($(NET_SNMP_CONFIG) --cflags | sed s,-I/usr/include,,)
LDFLAGS = -shared $$($(NET_SNMP_CONFIG) --ldflags)
CFLAGS += -I../include -I../libwr/include -I../ppsi/include -I../mini-rpc
CFLAGS += -I../include -I../libwr/include -I../ppsi/include -I../ppsi/arch-wrs/include
CFLAGS += -DWRS_WITH_SNMP_HACKISH_LOG=0
SHLIB = wrsSnmp.so
......
......@@ -7,7 +7,7 @@ WR-SWITCH-MIB DEFINITIONS ::= BEGIN
-- IMPORTS: Include definitions from other mibs here
IMPORTS
OBJECT-TYPE, Integer32, Counter32, Counter64
OBJECT-TYPE, Integer32, Counter32, Counter64,
MODULE-IDENTITY, enterprises FROM SNMPv2-SMI
DisplayString FROM SNMPv2-TC;
......@@ -17,7 +17,7 @@ CERN OBJECT IDENTIFIER ::= { enterprises 96 }
-- (I follow the CamelCase to be sure I don't mistype. Who knows the rules...
wrSwitchMIB MODULE-IDENTITY
LAST-UPDATED "201406181915Z" -- 18 june 2014, 19:15 (why saying again?)
LAST-UPDATED "201502231000Z"
ORGANIZATION "CERN"
CONTACT-INFO "postal: BE-CO-HT, CERN, Geneva
email: ht-drivers@cern.ch
......@@ -29,8 +29,8 @@ wrSwitchMIB MODULE-IDENTITY
-- Define typical mib nodes
-- we'll prefix everything in this mib with wrs (White Rabbit Switch)
wrsScalar OBJECT IDENTIFIER ::= { wrSwitchMIB 1 }
wrsPstats OBJECT IDENTIFIER ::= { wrSwitchMIB 2 }
wrsPpsi OBJECT IDENTIFIER ::= { wrSwitchMIB 3 }
wrsPstatsTable OBJECT IDENTIFIER ::= { wrSwitchMIB 2 }
wrsPpsi OBJECT IDENTIFIER ::= { wrSwitchMIB 3 } -- going to be obsolete
wrsVersion OBJECT IDENTIFIER ::= { wrSwitchMIB 4 }
wrsDate OBJECT IDENTIFIER ::= { wrSwitchMIB 5 }
......@@ -51,7 +51,7 @@ wrsScalarOne OBJECT-TYPE
-- code every time (I *hate* this obsolete crap: code generators should not
-- be like that).
wrsPstats OBJECT-TYPE
wrsPstatsTable OBJECT-TYPE
SYNTAX SEQUENCE OF pstatsEntry
MAX-ACCESS not-accessible
STATUS current
......@@ -67,7 +67,7 @@ pstatsEntry OBJECT-TYPE
DESCRIPTION
"An entry containing a pstat name and values for all WR ports."
INDEX { pstatsIndex }
::= { wrsPstats 1 }
::= { wrsPstatsTable 1 }
PstatsEntry ::=
SEQUENCE {
......@@ -244,33 +244,34 @@ pstatsWR17 OBJECT-TYPE
"The value of this counter for interface wr17."
::= {pstatsEntry 19 }
--following objects will be moved
wrsPpsiGlobalsX OBJECT IDENTIFIER ::= { wrsPpsi 3 }
wrsPortsTableX OBJECT IDENTIFIER ::= { wrsPpsi 4 }
wrsTemperatureX OBJECT IDENTIFIER ::= { wrsPpsi 5 }
-- The Wr/PTP/Ppsi information is two items: an array of scalars
-- and a table. In the table, the "channel" name is the row. But
-- the index is integer, following what ppsi does internally
wrsPpsiGlobals OBJECT IDENTIFIER ::= { wrsPpsi 1 }
wrsPpsiPerport OBJECT IDENTIFIER ::= { wrsPpsi 2 }
-- globals (.3)
-- globals (.3.1)
ppsiGrandmaterID OBJECT-TYPE
ptpGrandmasterIDX OBJECT-TYPE
SYNTAX OCTET STRING (SIZE(8))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The ClockID of the current grandmaster"
::= { wrsPpsiGlobals 1 }
::= { wrsPpsiGlobalsX 1 }
ppsiOwnID OBJECT-TYPE
ptpOwnIDX OBJECT-TYPE
SYNTAX OCTET STRING (SIZE(8))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The ClockID of this WR device"
::= { wrsPpsiGlobals 2 }
::= { wrsPpsiGlobalsX 2 }
ppsiMode OBJECT-TYPE
ptpModeX OBJECT-TYPE
SYNTAX INTEGER {
unknown(0), -- same as WRC_MODE macros
grandmaster(1),
......@@ -281,125 +282,339 @@ ppsiMode OBJECT-TYPE
STATUS current
DESCRIPTION
"The mode of this clock"
::= { wrsPpsiGlobals 3 }
::= { wrsPpsiGlobalsX 3 }
ppsiServoState OBJECT-TYPE
ptpServoStateX OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The servo state if slave"
::= { wrsPpsiGlobals 4 }
::= { wrsPpsiGlobalsX 4 }
ptpServoStateNX OBJECT-TYPE
SYNTAX INTEGER {
Uninitialized(0),
SYNC-NSEC(1), -- no idea how to keep "_" instead of "-"
SYNC-SEC(2),
SYNC-PHASE(3),
TRACK-PHASE(4),
WAIT-SYNC-IDLE(5),
WAIT-OFFSET-STABLE(6)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Numeric representation of servo state"
::= { wrsPpsiGlobalsX 5 }
ppsiPhaseTracking OBJECT-TYPE
ptpPhaseTrackingX OBJECT-TYPE
SYNTAX INTEGER {
not-tracking(0)
tracking(1)
NA(0),
not-tracking(1),
tracking(2)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether phase tracking is enabled in the servo"
::= { wrsPpsiGlobals 5 }
::= { wrsPpsiGlobalsX 6 }
ppsiSyncSource OBJECT-TYPE
ptpSyncSourceX OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The port name that is currently the synchronization source"
::= { wrsPpsiGlobals 6 }
::= { wrsPpsiGlobalsX 7 }
ppsiClockOffsetPs OBJECT-TYPE
ptpClockOffsetPsX OBJECT-TYPE
SYNTAX Counter64 -- should be integer64 (signed)
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Current clock offset from master, in picoseconds"
::= { wrsPpsiGlobals 7 }
::= { wrsPpsiGlobalsX 8 }
ppsiSkew OBJECT-TYPE
ptpClockOffsetPsHRX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Human Readable current clock offset from master, in picoseconds with saturation to integer"
::= { wrsPpsiGlobalsX 9 }
ptpSkewX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The estimated change of master-to-slave delay, in picoseconds"
::= { wrsPpsiGlobals 8 }
::= { wrsPpsiGlobalsX 10 }
ppsiRTT OBJECT-TYPE
ptpRTTX OBJECT-TYPE
SYNTAX Counter64 -- does unsigned64 exits?
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The round-trip-time, from master, in picoseconds"
::= { wrsPpsiGlobals 9 }
::= { wrsPpsiGlobalsX 11 }
ppsiLinkLength OBJECT-TYPE
ptpLinkLengthX OBJECT-TYPE
SYNTAX Unsigned32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Estimated fiber length, from master-to-slave delay, in meters"
::= { wrsPpsiGlobals 10 }
::= { wrsPpsiGlobalsX 12 }
ppsiServoUpdates OBJECT-TYPE
SYNTAX Counter64
ptpServoUpdatesX OBJECT-TYPE
SYNTAX Unsigned32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"How many time did the servo run"
::= { wrsPpsiGlobals 11 }
::= { wrsPpsiGlobalsX 13 }
ptpDeltaTxMX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Fixed Tx latency on Master side"
::= { wrsPpsiGlobalsX 14 }
-- per-port (.3.2)
ptpDeltaRxMX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Fixed Rx latency on Master side"
::= { wrsPpsiGlobalsX 15 }
ppsiPort OBJECT-TYPE
SYNTAX PpsiPort
ptpDeltaTxSX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Fixed Tx latency on Slave side"
::= { wrsPpsiGlobalsX 16 }
ptpDeltaRxSX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Fixed Rx latency on Slave side"
::= { wrsPpsiGlobalsX 17 }
-- per-port (.4)
wrsPortsTableX OBJECT-TYPE
SYNTAX SEQUENCE OF wrsPortsEntryX
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Information for each port"
::= { wrsPpsi 4 }
wrsPortsEntryX OBJECT-TYPE
SYNTAX WrsPortsEntryX
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"The ppsi line: information for each port"
INDEX { portIndex }
::= { wrsPpsiPerport 1 }
"An entry containing ports statuses"
INDEX { wrsPortIndex }
::= { wrsPortsTableX 1 }
PpsiPort ::=
WrsPortsEntryX ::=
SEQUENCE {
portLink INTEGER
portMode INTEGER
portLocked INTEGER {not-locked(0), locked(1)},
portPeer OCTET STRING
portLink INTEGER,
portMode INTEGER,
portLocked INTEGER,
portPeer OCTET STRING,
portSfpVN DisplayString,
portSfpPN DisplayString,
portSfpVS DisplayString,
portSfpInDB INTEGER,
portSfpGbE INTEGER,
portSfpError INTEGER
}
portLink OBJECT-TYPE
SYNTAX INTEGER {down(0), up(1)}
portLinkX OBJECT-TYPE
SYNTAX INTEGER {
NA(0),
down(1),
up(2)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether the link is up or down"
::= { ppsiPort 1 }
::= { wrsPortsEntryX 1 }
portMode OBJECT-TYPE
SYNTAX INTEGER {slave(0), master(1)}
portModeX OBJECT-TYPE
SYNTAX INTEGER {
unknown(0),
master(1),
slave(2),
non-wr(3),
auto(4)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether the port is master or slave"
::= { ppsiPort 2 }
::= { wrsPortsEntryX 2 }
portLocked OBJECT-TYPE
SYNTAX INTEGER {not-locked(0), locked(1)}
portLockedX OBJECT-TYPE
SYNTAX INTEGER {
NA(0),
not-locked(1),
locked(2)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether the peers are locked or not"
::= { ppsiPort 3 }
::= { wrsPortsEntryX 3 }
portPeer OBJECT-TYPE
portPeerX OBJECT-TYPE
SYNTAX OCTET STRING (SIZE(8))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"the ClockID of the peer, if available. Or 0"
::= { ppsiPort 4 }
::= { wrsPortsEntryX 4 }
portSfpVNX OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Vendor Name of SFP"
::= { wrsPortsEntryX 5 }
portSfpPNX OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Part Number of SFP"
::= { wrsPortsEntryX 6 }
portSfpVSX OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Vendor serial number of SFP"
::= { wrsPortsEntryX 7 }
portSfpInDBX OBJECT-TYPE
SYNTAX INTEGER {NA(0), not-in-Data-Base(1), in-Data-Base(2)}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether the SFP is in data base or not"
::= { wrsPortsEntryX 8 }
portSfpGbEX OBJECT-TYPE
SYNTAX INTEGER {NA(0), not-GbE(1), GbE(2)}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Whether the SFP is 1 GbE"
::= { wrsPortsEntryX 9 }
portSfpErrorX OBJECT-TYPE
SYNTAX INTEGER {
NA(0),
SFP-ok(1),
SFP-Error(2)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Problem with SFP configuration. Port has to be always 1GbE. Port has to be in data base as long as support WR."
::= { wrsPortsEntryX 10 }
-- wrsTemperatureX (.5)
tempFPGAX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"FPGA temperature"
::= { wrsTemperatureX 1 }
tempPLLX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"PLL temperature"
::= { wrsTemperatureX 2 }
tempPSLX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Power Supply Left (PSL) temperature"
::= { wrsTemperatureX 3 }
tempPSRX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Power Supply Right (PSR) temperature"
::= { wrsTemperatureX 4 }
tempTholdFPGAX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Threshold level for FPGA temperature"
::= { wrsTemperatureX 5 }
tempTholdPLLX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Threshold level for PLL temperature"
::= { wrsTemperatureX 6 }
tempTholdPSLX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Threshold level for Power Supply Left (PSL) temperature"
::= { wrsTemperatureX 7 }
tempTholdPSRX OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Threshold level for Power Supply Right (PSR) temperature"
::= { wrsTemperatureX 8 }
tempWarningX OBJECT-TYPE
SYNTAX INTEGER {
NA(0),
Threshold-not-set(1),
Temperature-OK(2),
Temperature-too-high(3),
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Warning if temperature exceed threshold levels"
::= { wrsTemperatureX 9 }
-- Versions (4) are all just strings, several of them
......
......@@ -18,21 +18,3 @@ void init_wrsSnmp(void)
init_wrsVersion();
init_wrsDate();
}
/* open a file or a pipe, so I test with files, and run with pipes */
FILE *wrs_fpopen(char *file_or_pipe, char *mode)
{
if (file_or_pipe[0] == '|')
return popen(file_or_pipe + 1, mode);
else
return fopen(file_or_pipe, mode);
}
void wrs_fpclose(FILE *f, char *file_or_pipe)
{
if (file_or_pipe[0] == '|')
pclose(f);
else
fclose(f);
}
......@@ -3,6 +3,7 @@
*
* Alessandro Rubini for CERN, 2014
*/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
......@@ -12,66 +13,82 @@
#undef FALSE
#undef TRUE
/* conflict between definition in net-snmp-agent-includes.h (which include
* snmp_vars.h) and ppsi.h where INST is defined as a inline function */
#undef INST
#include <ppsi/ieee1588_types.h> /* for ClockIdentity */
#include <minipc.h>
#include <libwr/shmem.h>
#include <ppsi/ppsi.h>
#include <libwr/hal_shmem.h>
#include <stdio.h>
#include "wrsSnmp.h"
#define PPSI_CACHE_TIMEOUT 5 /* 1 second: refresh table every so often */
/* Table-driven memcpy: declare how to pick fields (pickinfo) -- and scan */
/* Table-driven memcpy: declare how to pick fields (pickinfo) */
struct ppsi_pickinfo {
/* Following fields are used to format the output */
int type; int offset; int len;
/* The following field is used to scan the input */
char *name;
};
static struct wrs_shm_head *hal_head;
static struct hal_shmem_header *hal_shmem;
static struct hal_port_state *hal_ports;
static int hal_nports_local;
#define FIELD(_struct, _type, _field, _name) { \
.name = _name, \
static struct wrs_shm_head *ppsi_head;
static struct pp_globals *ppg;
struct wr_servo_state_t *ppsi_servo;
#define FIELD(_struct, _type, _field) { \
.type = _type, \
.offset = offsetof(struct _struct, _field), \
.len = sizeof(_struct._field), \
.name = _name, /* Warning: see wr_mon */ \
}
/* Our data: globals */
static struct wrs_p_globals {
ClockIdentity gm_id; /* Scanned as %x:... because it's 8-long */
ClockIdentity my_id; /* Same as above */
int ppsi_mode;
char ppsi_servo_state[32]; /* Scanned as "%s" */
int phase_tracking;
char sync_source[32]; /* Scanned as "%s" because length > 8 bytes */
ClockIdentity gm_id; /* FIXME: not implemented */
ClockIdentity my_id; /* FIXME: not implemented */
int ppsi_mode; /* FIXME: not implemented */
char servo_state_name[32]; /* State as string */
int servo_state; /* state number */
int tracking_enabled;
char sync_source[32]; /* FIXME: not implemented */
int64_t clock_offset;
int32_t clock_offsetHR; /* Human readable version of clock_offset,
* saturated to int limits */
int32_t skew;
int64_t rtt;
uint32_t llength;
int64_t servo_updates;
uint32_t servo_updates;
int32_t delta_tx_m;
int32_t delta_rx_m;
int32_t delta_tx_s;
int32_t delta_rx_s;
} wrs_p_globals;
static struct ppsi_pickinfo g_pickinfo[] = {
/* Warning: strings are a special case for snmp format */
FIELD(wrs_p_globals, ASN_OCTET_STR, gm_id, "gm_id:"),
FIELD(wrs_p_globals, ASN_OCTET_STR, my_id, "clock_id:"),
FIELD(wrs_p_globals, ASN_INTEGER, ppsi_mode, "mode:"),
FIELD(wrs_p_globals, ASN_OCTET_STR, ppsi_servo_state, "servo_state:"),
FIELD(wrs_p_globals, ASN_INTEGER, phase_tracking, "tracking:"),
FIELD(wrs_p_globals, ASN_OCTET_STR, sync_source, "source:"),
FIELD(wrs_p_globals, ASN_COUNTER64, clock_offset, "ck_offset:"),
FIELD(wrs_p_globals, ASN_INTEGER, skew, "skew:"),
FIELD(wrs_p_globals, ASN_COUNTER64, rtt, "rtt:"),
FIELD(wrs_p_globals, ASN_UNSIGNED, llength, "llength:"),
FIELD(wrs_p_globals, ASN_COUNTER64, servo_updates, "servo_upd:"),
FIELD(wrs_p_globals, ASN_OCTET_STR, gm_id),
FIELD(wrs_p_globals, ASN_OCTET_STR, my_id),
FIELD(wrs_p_globals, ASN_INTEGER, ppsi_mode),
FIELD(wrs_p_globals, ASN_OCTET_STR, servo_state_name),
FIELD(wrs_p_globals, ASN_INTEGER, servo_state),
FIELD(wrs_p_globals, ASN_INTEGER, tracking_enabled),
FIELD(wrs_p_globals, ASN_OCTET_STR, sync_source),
FIELD(wrs_p_globals, ASN_COUNTER64, clock_offset),
FIELD(wrs_p_globals, ASN_INTEGER, clock_offsetHR),
FIELD(wrs_p_globals, ASN_INTEGER, skew),
FIELD(wrs_p_globals, ASN_COUNTER64, rtt),
FIELD(wrs_p_globals, ASN_UNSIGNED, llength),
FIELD(wrs_p_globals, ASN_UNSIGNED, servo_updates),
FIELD(wrs_p_globals, ASN_INTEGER, delta_tx_m),
FIELD(wrs_p_globals, ASN_INTEGER, delta_rx_m),
FIELD(wrs_p_globals, ASN_INTEGER, delta_tx_s),
FIELD(wrs_p_globals, ASN_INTEGER, delta_rx_s),
};
/* Our data: per-port information */
......@@ -81,138 +98,210 @@ static struct wrs_p_perport {
unsigned link_up;
unsigned port_mode;
unsigned port_locked;
char sfp_vn[16]; /* vendor name */
char sfp_pn[16]; /* part name */
char sfp_vs[16]; /* vendor serial */
int sfp_in_db;
int sfp_GbE;
int sfp_error;
} wrs_p_perport, wrs_p_array[WRS_N_PORTS];
static struct ppsi_pickinfo p_pickinfo[] = {
FIELD(wrs_p_perport, ASN_INTEGER, link_up, "linkup:"),
FIELD(wrs_p_perport, ASN_INTEGER, port_mode, "mode:"),
FIELD(wrs_p_perport, ASN_INTEGER, port_locked, "locked:"),
FIELD(wrs_p_perport, ASN_OCTET_STR, peer_id, "peer_id:"),
FIELD(wrs_p_perport, ASN_INTEGER, link_up),
FIELD(wrs_p_perport, ASN_INTEGER, port_mode),
FIELD(wrs_p_perport, ASN_INTEGER, port_locked),
FIELD(wrs_p_perport, ASN_OCTET_STR, peer_id),
FIELD(wrs_p_perport, ASN_OCTET_STR, sfp_vn),
FIELD(wrs_p_perport, ASN_OCTET_STR, sfp_pn),
FIELD(wrs_p_perport, ASN_OCTET_STR, sfp_vs),
FIELD(wrs_p_perport, ASN_INTEGER, sfp_in_db),
FIELD(wrs_p_perport, ASN_INTEGER, sfp_GbE),
FIELD(wrs_p_perport, ASN_INTEGER, sfp_error),
};
/* Our data: globals */
static struct wrs_temperatures {
int temp_fpga; /* FPGA temperature */
int temp_pll; /* PLL temperature */
int temp_psl; /* PSL temperature */
int temp_psr; /* PSR temperature */
int temp_fpga_thold; /* Threshold value for FPGA temperature */
int temp_pll_thold; /* Threshold value for PLL temperature */
int temp_psl_thold; /* Threshold value for PSL temperature */
int temp_psr_thold; /* Threshold value for PSR temperature */
int temp_warning; /* Warning when at least one temperature
* threshold level exceeded */
} wrs_temperatures;
static struct ppsi_pickinfo temp_pickinfo[] = {
FIELD(wrs_temperatures, ASN_INTEGER, temp_fpga),
FIELD(wrs_temperatures, ASN_INTEGER, temp_pll),
FIELD(wrs_temperatures, ASN_INTEGER, temp_psl),
FIELD(wrs_temperatures, ASN_INTEGER, temp_psr),
FIELD(wrs_temperatures, ASN_INTEGER, temp_fpga_thold),
FIELD(wrs_temperatures, ASN_INTEGER, temp_pll_thold),
FIELD(wrs_temperatures, ASN_INTEGER, temp_psl_thold),
FIELD(wrs_temperatures, ASN_INTEGER, temp_psr_thold),
FIELD(wrs_temperatures, ASN_INTEGER, temp_warning),
};
/* Parse a single line, used by both global and per-port */
static void wrs_ppsi_parse_line(char *line, void *baseaddr,
struct ppsi_pickinfo *pi, int npi)
static int32_t int_saturate(int64_t value)
{
char key[20], value[60];
void *addr;
long long *ptr64;
int i;
i = sscanf(line, "%s %s", key, value); /* value string has no spaces */
logmsg("%s", line);
logmsg("--%s--%s--\n", key, value);
if (i == 0)
return;
if (i != 2) {
snmp_log(LOG_ERR, "%s: can't parse line: %s", __func__,
line /* this includes a trailing newline already */);
return;
}
/* now use parseinfo to find the key */
for (i = 0; i < npi; i++, pi++)
if (!strcmp(key, pi->name))
break;
if (i == npi) {
snmp_log(LOG_ERR, "%s: can't find key \"%s\"\n", __func__,
key);
return;
}
if (value >= INT32_MAX)
return INT32_MAX;
else if (value <= INT32_MIN)
return INT32_MIN;
addr = baseaddr + pi->offset;
return value;
}
/* Here I'm lazy in error checking, let's hope it's ok */
switch (pi->type) {
case ASN_UNSIGNED:
/*
* our unsigned is line length, definitely less than 2G,
* so fall through...
*/
case ASN_INTEGER:
sscanf(value, "%i", (int *)addr);
break;
static void wrs_ppsi_get_globals(void)
{
unsigned ii;
unsigned retries = 0;
case ASN_COUNTER64:
ptr64 = addr;
sscanf(value, "%lli", ptr64);
memset(&wrs_p_globals, 0, sizeof(wrs_p_globals));
while (1) {
ii = wrs_shm_seqbegin(ppsi_head);
strncpy(wrs_p_globals.servo_state_name,
ppsi_servo->servo_state_name,
sizeof(ppsi_servo->servo_state_name));
wrs_p_globals.servo_state = ppsi_servo->state;
/* Keep value 0 for Not available */
wrs_p_globals.tracking_enabled =
1 + ppsi_servo->tracking_enabled;
/*
* WARNING: the current snmpd is bugged: it has
* endianness problems with 64 bit, and the two
* halves are swapped. So pre-swap them here
*/
*ptr64 = (*ptr64 << 32) | (*ptr64 >> 32);
break;
case ASN_OCTET_STR:
if (pi->len == 8) {
char *a = addr;
sscanf(value,
"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
a+0, a+1, a+2, a+3, a+4, a+5, a+6, a+7);
break;
wrs_p_globals.rtt = (ppsi_servo->picos_mu << 32)
| (ppsi_servo->picos_mu >> 32);
wrs_p_globals.clock_offset = (ppsi_servo->offset << 32)
| (ppsi_servo->offset >> 32);
wrs_p_globals.clock_offsetHR =
int_saturate(ppsi_servo->offset);
wrs_p_globals.skew = ppsi_servo->skew;
wrs_p_globals.llength = (uint32_t)(ppsi_servo->delta_ms/1e12 *
300e6 / 1.55);
wrs_p_globals.servo_updates = ppsi_servo->update_count;
wrs_p_globals.delta_tx_m = ppsi_servo->delta_tx_m;
wrs_p_globals.delta_rx_m = ppsi_servo->delta_rx_m;
wrs_p_globals.delta_tx_s = ppsi_servo->delta_tx_s;
wrs_p_globals.delta_rx_s = ppsi_servo->delta_rx_s;
retries++;
if (retries > 100) {
snmp_log(LOG_ERR, "%s: too many retries to read PPSI\n",
__func__);
retries = 0;
}
if (pi->len > 8) {
strcpy(addr, value);
break;
}
snmp_log(LOG_ERR, "%s: no rule to parse OCTET_STREAM \"%s\"\n",
__func__, key);
break;
default:
snmp_log(LOG_ERR, "%s: no rule to parse type of key \"%s\"\n",
__func__, key);
if (!wrs_shm_seqretry(ppsi_head, ii))
break; /* consistent read */
usleep(1000);
}
}
static void wrs_ppsi_get_globals(void)
static void wrs_get_temperatures(void)
{
static char *fname;
FILE *f;
char s[80];
/* Allow the environment to override the fname, during development */
if (!fname) {
fname = getenv("WRS_SNMP_MON_FNAME");
if (!fname)
fname = "|/wr/bin/wr_mon -g";
}
unsigned ii;
unsigned retries = 0;
f = wrs_fpopen(fname, "r");
if (!f) {
snmp_log(LOG_ERR, "%s: can't open \"%s\"\n", __func__, fname);
return;
memset(&wrs_temperatures, 0, sizeof(wrs_temperatures));
while (1) {
ii = wrs_shm_seqbegin(ppsi_head);
wrs_temperatures.temp_fpga = hal_shmem->temp.fpga >> 8;
wrs_temperatures.temp_pll = hal_shmem->temp.pll >> 8;
wrs_temperatures.temp_psl = hal_shmem->temp.psl >> 8;
wrs_temperatures.temp_psr = hal_shmem->temp.psr >> 8;
wrs_temperatures.temp_fpga_thold = hal_shmem->temp.fpga_thold;
wrs_temperatures.temp_pll_thold = hal_shmem->temp.pll_thold;
wrs_temperatures.temp_psl_thold = hal_shmem->temp.psl_thold;
wrs_temperatures.temp_psr_thold = hal_shmem->temp.psr_thold;
if (!wrs_temperatures.temp_fpga_thold
&& !wrs_temperatures.temp_pll_thold
&& !wrs_temperatures.temp_psl_thold
&& !wrs_temperatures.temp_psr_thold) {
/* no threshold are set */
wrs_temperatures.temp_warning = 1;
} else {
/* rise warning when at least one threshold level
* is exceeded, add 2, since 0 is readings not
* available, 1 is no threshold set */
wrs_temperatures.temp_warning = 2 +
(wrs_temperatures.temp_fpga > wrs_temperatures.temp_fpga_thold)
|| (wrs_temperatures.temp_pll > wrs_temperatures.temp_pll_thold)
|| (wrs_temperatures.temp_psl > wrs_temperatures.temp_psl_thold)
|| (wrs_temperatures.temp_psr > wrs_temperatures.temp_psr_thold);
}
memset(&wrs_p_globals, 0, sizeof(wrs_p_globals));
while (fgets(s, sizeof(s), f)) {
wrs_ppsi_parse_line(s, &wrs_p_globals, g_pickinfo,
ARRAY_SIZE(g_pickinfo));
retries++;
if (retries > 100) {
snmp_log(LOG_ERR, "%s: too many retries to read PPSI\n",
__func__);
retries = 0;
}
if (!wrs_shm_seqretry(ppsi_head, ii))
break; /* consistent read */
usleep(1000);
}
wrs_fpclose(f, fname);
}
void init_shm(void)
{
struct hal_shmem_header *h;
hal_head = wrs_shm_get(wrs_shm_hal, "", WRS_SHM_READ);
if (!hal_head) {
fprintf(stderr, "unable to open shm for HAL!\n");
snmp_log(LOG_ERR, "unable to open shm for HAL!\n");
exit(-1);
}
/* check hal's shm version */
if (hal_head->version != HAL_SHMEM_VERSION) {
snmp_log(LOG_ERR, "unknown hal's shm version %i "
"(known is %i)\n", hal_head->version,
HAL_SHMEM_VERSION);
exit(-1);
}
h = (void *)hal_head + hal_head->data_off;
hal_shmem = (void *)hal_head + hal_head->data_off;
/* Assume number of ports does not change in runtime */
hal_nports_local = h->nports;
/* Even after HAL restart, HAL will place structures at the same
* addresses. No need to re-dereference pointer at each read. */
hal_ports = wrs_shm_follow(hal_head, h->ports);
hal_nports_local = hal_shmem->nports;
if (hal_nports_local > WRS_N_PORTS) {
snmp_log(LOG_ERR, "Too many ports reported by HAL. "
"%d vs %d supported\n",
hal_nports_local, WRS_N_PORTS);
exit(-1);
}
/* Even after HAL restart, HAL will place structures at the same
* addresses. No need to re-dereference pointer at each read. */
hal_ports = wrs_shm_follow(hal_head, hal_shmem->ports);
if (!hal_ports) {
snmp_log(LOG_ERR, "Unalbe to follow hal_ports pointer in HAL's"
" shmem");
exit(-1);
}
ppsi_head = wrs_shm_get(wrs_shm_ptp, "", WRS_SHM_READ);
if (!ppsi_head) {
snmp_log(LOG_ERR, "unable to open shm for PPSI!\n");
exit(-1);
}
/* check hal's shm version */
if (ppsi_head->version != WRS_PPSI_SHMEM_VERSION) {
snmp_log(LOG_ERR, "wr_mon: unknown PPSI's shm version %i "
"(known is %i)\n",
ppsi_head->version, WRS_PPSI_SHMEM_VERSION);
exit(-1);
}
ppg = (void *)ppsi_head + ppsi_head->data_off;
ppsi_servo = wrs_shm_follow(ppsi_head, ppg->global_ext_data);
if (!ppsi_servo) {
snmp_log(LOG_ERR, "Cannot follow ppsi_servo in shmem.\n");
exit(-1);
}
}
static void wrs_ppsi_get_per_port(void)
......@@ -223,7 +312,7 @@ static void wrs_ppsi_get_per_port(void)
/* read data, with the sequential lock to have all data consistent */
struct hal_port_state *port_state;
memset(wrs_p_array, 0, sizeof(wrs_p_array));
do {
while (1) {
ii = wrs_shm_seqbegin(hal_head);
for (i = 0; i < hal_nports_local; ++i) {
/* Assume that number of ports does not change between
......@@ -234,14 +323,41 @@ static void wrs_ppsi_get_per_port(void)
port_state = hal_lookup_port(hal_ports,
hal_nports_local, if_name);
/* No need to copy all ports structures, only what
* we're interested in */
wrs_p_array[i].link_up = state_up(port_state->state);
wrs_p_array[i].port_mode = (port_state->mode ==
HEXP_PORT_MODE_WR_SLAVE ? 0 : 1);
wrs_p_array[i].port_locked = port_state->locked;
* we're interested in.
* Keep value 0 for Not available */
wrs_p_array[i].link_up =
1 + state_up(port_state->state);
wrs_p_array[i].port_mode = port_state->mode;
if (port_state->state == HAL_PORT_STATE_DISABLED)
/* if port is disabled don't fill
* other fields */
continue;
/* Keep value 0 for Not available */
wrs_p_array[i].port_locked = 1 + port_state->locked;
/* FIXME: get real peer_id */
memset(&wrs_p_array[i].peer_id, 0xff,
sizeof(ClockIdentity));
wrs_p_array[i].sfp_in_db =
port_state->calib.sfp.flags & SFP_FLAG_IN_DB ? 2 : 1;
wrs_p_array[i].sfp_GbE =
port_state->calib.sfp.flags & SFP_FLAG_1GbE ? 2 : 1;
strncpy(wrs_p_array[i].sfp_vn,
port_state->calib.sfp.vendor_name,
sizeof(wrs_p_array[i].sfp_vn));
strncpy(wrs_p_array[i].sfp_pn,
port_state->calib.sfp.part_num,
sizeof(wrs_p_array[i].sfp_pn));
strncpy(wrs_p_array[i].sfp_vs,
port_state->calib.sfp.vendor_serial,
sizeof(wrs_p_array[i].sfp_vs));
/* sfp error when SFP is not 1 GbE or
* (port is not wr-non mode and sfp not in data base)
* Keep value 0 for Not available */
wrs_p_array[i].sfp_error = 1 +
(wrs_p_array[i].sfp_GbE == 1) ||
((port_state->mode != HEXP_PORT_MODE_NON_WR) &&
(wrs_p_array[i].sfp_in_db == 1));
logmsg("reading ports name %s link %d, mode %d, "
"locked %d\n", port_state->name,
wrs_p_array[i].link_up, wrs_p_array[i].port_mode,
......@@ -254,7 +370,10 @@ static void wrs_ppsi_get_per_port(void)
__func__);
retries = 0;
}
} while (wrs_shm_seqretry(hal_head, ii));
if (!wrs_shm_seqretry(hal_head, ii))
break; /* consistent read */
usleep(1000);
}
}
......@@ -301,7 +420,7 @@ static int ppsi_g_group(netsnmp_mib_handler *handler,
ptr = (void *)&wrs_p_globals + pi->offset;
len = pi->len;
if (len > 8) /* special case for strings */
len = strlen(ptr);
len = strnlen(ptr, len);
snmp_set_var_typed_value(requests->requestvb,
pi->type, ptr, len);
break;
......@@ -313,6 +432,57 @@ static int ppsi_g_group(netsnmp_mib_handler *handler,
return SNMP_ERR_NOERROR;
}
/* This is the filler for the temperature sensors */
static int temp_group(netsnmp_mib_handler *handler,
netsnmp_handler_registration *reginfo,
netsnmp_agent_request_info *reqinfo,
netsnmp_request_info *requests)
{
static time_t t0, t1;
int obj; /* the final index */
struct ppsi_pickinfo *pi;
void *ptr;
int len;
/*
* Retrieve information from ppsi itself. But this function
* is called once for every item, so only query the whole set
* once every 2 seconds.
*/
t1 = time(NULL);
if (!t0 || t1 - t0 > 1) {
wrs_get_temperatures();
t0 = t1;
}
switch (reqinfo->mode) {
case MODE_GET:
/* "- 2" because last is 0 for all scalars, I suppose */
obj = requests->requestvb->name[
requests->requestvb->name_length - 2];
obj--; /* we are 0-based */
if (obj < 0 || obj >= ARRAY_SIZE(temp_pickinfo)) {
snmp_log(LOG_ERR, "wrong index (%d) in wrs ppsi\n",
obj + 1);
return SNMP_ERR_GENERR;
}
pi = temp_pickinfo + obj;
ptr = (void *)&wrs_temperatures + pi->offset;
len = pi->len;
if (len > 8) /* special case for strings */
len = strnlen(ptr, len);
snmp_set_var_typed_value(requests->requestvb,
pi->type, ptr, len);
break;
default:
snmp_log(LOG_ERR, "unknown mode (%d) in wrs temp group\n",
reqinfo->mode);
return SNMP_ERR_GENERR;
}
return SNMP_ERR_NOERROR;
}
/* For the per-port table we use an iterator like in wrsPstats.c */
static netsnmp_variable_list *
......@@ -332,8 +502,10 @@ ppsi_p_next_entry(void **loop_context,
/* Create the row OID: only the counter index */
snmp_set_var_value(index, (u_char *)&i, sizeof(i));
/* Set the data context (1..4 -> 0..3) */
*data_context = (void *)(intptr_t)(i - 1);
/* Set the data context (1..4)
* Cannot be set to 0, because netsnmp_extract_iterator_context returns
* NULL in function wrsPstats_handler when table is over */
*data_context = (void *)i;
/* and set the loop context for the next iteration */
*loop_context = (void *)i;
return index;
......@@ -370,6 +542,8 @@ ppsi_p_handler(netsnmp_mib_handler *handler,
struct wrs_p_perport *data = wrs_p_array; /* a shorter name */
struct ppsi_pickinfo *pi;
int wrport, subid;
int len;
void *ptr;
//logmsg("%s: %i\n", __func__, __LINE__);
switch (reqinfo->mode) {
......@@ -400,13 +574,24 @@ ppsi_p_handler(netsnmp_mib_handler *handler,
/* "context" is the port number */
wrport = (intptr_t)netsnmp_extract_iterator_context(request);
if (!wrport)
/* NULL returned from
* netsnmp_extract_iterator_context shuld be
* interpreted as end of table */
break;
/* change range of wrport (1..4 (snmp is 1 based) ->
* 0..3 (wrs_p_array/data array is 0 based)) */
wrport--;
table_info = netsnmp_extract_table_info(request);
subid = table_info->colnum - 1;
pi = p_pickinfo + subid;
snmp_set_var_typed_value(requestvb, pi->type,
(void *)(data + wrport)
+ pi->offset, pi->len);
ptr = (void *)(data + wrport) + pi->offset;
len = pi->len;
if (len > 8) /* special case for strings */
len = strnlen(ptr, len);
snmp_set_var_typed_value(requestvb, pi->type, ptr, len);
}
return SNMP_ERR_NOERROR;
}
......@@ -414,10 +599,12 @@ ppsi_p_handler(netsnmp_mib_handler *handler,
void
init_wrsPpsi(void)
{
const oid wrsPpsiG_oid[] = { WRS_OID, 3, 1 };
const oid wrsPpsiG_oid[] = { WRS_OID, 3, 3 };
netsnmp_handler_registration *hreg;
/* Above for globals, below for per-port */
const oid wrsPpsiP_oid[] = { WRS_OID, 3, 2 };
const oid wrsPpsiP_oid[] = { WRS_OID, 3, 4 };
netsnmp_handler_registration *hreg_temp;
const oid wrsTemperature_oid[] = { WRS_OID, 3, 5 };
netsnmp_table_registration_info *table_info;
netsnmp_iterator_info *iinfo;
netsnmp_handler_registration *reginfo;
......@@ -467,4 +654,13 @@ init_wrsPpsi(void)
ppsi_p_load, NULL,
wrsPpsiP_oid,
OID_LENGTH(wrsPpsiP_oid)));
/* do the registration for the scalars/globals */
hreg_temp = netsnmp_create_handler_registration(
"wrsTemperature", temp_group,
wrsTemperature_oid, OID_LENGTH(wrsTemperature_oid),
HANDLER_CAN_RONLY);
netsnmp_register_scalar_group(
hreg_temp, 1 /* min */, ARRAY_SIZE(temp_pickinfo) /* max */);
}
......@@ -57,9 +57,18 @@ wrsPstats_handler(netsnmp_mib_handler *handler,
logmsg("%s: %i\n", __func__, __LINE__);
/* our "context" is the counter number; "subid" the column i.e. the port */
counter = (intptr_t)netsnmp_extract_iterator_context(request);
if (!counter)
/* NULL returned from
* netsnmp_extract_iterator_context shuld be
* interpreted as end of table */
continue;
/* change range of counter (1..39 (snmp is 1 based) ->
* 0..38 (pstats_global_data array is 0 based)) */
counter--;
table_info = netsnmp_extract_table_info(request);
wrport = table_info->colnum - 2; /* port is 0-based and position 1 is the string */
/* port is 0-based and position 1 is the string */
wrport = table_info->colnum - 2;
logmsg("counter %i, port %i\n", counter, wrport);
if (wrport < 0) {
......@@ -110,8 +119,10 @@ wrsPstats_next_entry(void **loop_context,
/* Create the row OID: only the counter index */
snmp_set_var_value(index, (u_char *)&i, sizeof(i));
/* Set the data context (1..39 -> 0..38) */
*data_context = (void *)(intptr_t)(i - 1);
/* Set the data context (1..39)
* Cannot be set to 0, because netsnmp_extract_iterator_context returns
* NULL in function wrsPstats_handler when table is over */
*data_context = (void *)i;
/* and set the loop context for the next iteration */
*loop_context = (void *)i;
return index;
......
......@@ -85,8 +85,4 @@ extern void init_wrsDate(void);
#define WRS_OID 1, 3, 6, 1, 4, 1, 96, 100
/* Open a file or a pipe according to name[0] (e.g. "|wr_mon", "/tmp/log") */
extern FILE *wrs_fpopen(char *file_or_pipe, char *mode);
extern void wrs_fpclose(FILE *f, char *file_or_pipe);
#endif /* WRS_SNMP_H */
......@@ -23,33 +23,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libwr/util.h>
#include <libwr/shmem.h>
#include <libwr/hal_shmem.h>
#include <libwr/rtu_shmem.h>
#include <minipc.h>
#include <rtud_exports.h>
#include <mac.h>
#include <errno.h>
#define MINIPC_TIMEOUT 200
static struct minipc_ch *rtud_ch;
// forwarding entries
void rtudexp_get_fd_list(rtudexp_fd_list_t *list, int start_from)
{
minipc_call(rtud_ch, MINIPC_TIMEOUT, &rtud_export_get_fd_list, list,
start_from);
}
// vlan entries
void rtudexp_get_vd_list(rtudexp_vd_list_t *list, int current)
{
minipc_call(rtud_ch, MINIPC_TIMEOUT, &rtud_export_get_vd_list, list,
current);
}
struct wrs_shm_head *rtu_port_shmem;
static struct rtu_vlan_table_entry vlan_tab_local[NUM_VLANS];
static struct rtu_filtering_entry rtu_htab_local[RTU_BUCKETS * HTAB_ENTRIES];
int rtudexp_clear_entries(int netif, int force)
{
......@@ -76,42 +69,6 @@ int rtudexp_vlan_entry(int vid, int fid, const char *ch_mask, int drop, int prio
return (ret<0)?ret:val;
}
#define RTU_MAX_ENTRIES 8192
#define NUM_VLANS 4096
void fetch_rtu_fd(rtudexp_fd_entry_t *d, int *n_entries)
{
int start = 0, n = 0;
rtudexp_fd_list_t list;
do {
rtudexp_get_fd_list(&list, start);
// printf("num_rules %d\n", list.num_rules);
memcpy( d+n, list.list, sizeof(rtudexp_fd_entry_t) * list.num_rules);
start=list.next;
n+=list.num_rules;
} while(start > 0);
// printf("%d rules \n", n);
*n_entries = n;
}
int fetch_rtu_vd(rtudexp_vd_entry_t *d, int *n_entries)
{
int start = 0, n = 0;
rtudexp_vd_list_t list;
do {
rtudexp_get_vd_list(&list, start);
memcpy( d+n, list.list, sizeof(rtudexp_vd_entry_t) * list.num_entries);
start=list.next;
n+=list.num_entries;
} while(start > 0);
*n_entries = n;
return 0;
}
/**
* \brief Write mac address into a buffer to avoid concurrent access on static variable.
*/
......@@ -123,15 +80,12 @@ char *mac_to_buffer(uint8_t mac[ETH_ALEN],char buffer[ETH_ALEN_STR])
return buffer;
}
static int cmp_entries(const void *p1, const void *p2)
static int cmp_rtu_entries(const void *p1, const void *p2)
{
rtudexp_fd_entry_t *e1 = (rtudexp_fd_entry_t *)p1;
rtudexp_fd_entry_t *e2 = (rtudexp_fd_entry_t *)p2;
const struct rtu_filtering_entry *e1 = p1;
const struct rtu_filtering_entry *e2 = p2;
return memcmp(e1->mac, e2->mac, 6);
// return strcmp(* (char * const *) p1, * (char * const *) p2);
}
char *decode_ports(int dpm, int nports)
......@@ -185,6 +139,13 @@ int get_nports_from_hal(void)
fprintf(stderr, "unable to open shm for HAL!\n");
exit(-1);
}
/* check hal's shm version */
if (hal_head->version != HAL_SHMEM_VERSION) {
fprintf(stderr, "rtu_stat: unknown hal's shm version %i "
"(known is %i)\n",
hal_head->version, HAL_SHMEM_VERSION);
exit(-1);
}
h = (void *)hal_head + hal_head->data_off;
/* Assume number of ports does not change in runtime */
hal_nports_local = h->nports;
......@@ -197,26 +158,119 @@ int get_nports_from_hal(void)
return hal_nports_local;
}
int main(int argc, char **argv)
int read_vlans(void)
{
unsigned ii;
unsigned retries = 0;
struct rtu_vlan_table_entry *vlan_tab_shm;
struct rtu_shmem_header *rtu_hdr;
rtu_hdr = (void *)rtu_port_shmem + rtu_port_shmem->data_off;
vlan_tab_shm = wrs_shm_follow(rtu_port_shmem, rtu_hdr->vlans);
if (!vlan_tab_shm)
return -2;
/* read data, with the sequential lock to have all data consistent */
while (1) {
ii = wrs_shm_seqbegin(rtu_port_shmem);
memcpy(&vlan_tab_local, vlan_tab_shm,
NUM_VLANS * sizeof(*vlan_tab_shm));
retries++;
if (retries > 100)
return -1;
if (!wrs_shm_seqretry(rtu_port_shmem, ii))
break; /* consistent read */
usleep(1000);
}
return 0;
}
/* Read filtes from rtud's shm, convert hashtable to regular table */
int read_htab(int *read_entries)
{
unsigned ii;
unsigned retries = 0;
struct rtu_filtering_entry *htab_shm;
struct rtu_shmem_header *rtu_hdr;
struct rtu_filtering_entry *empty;
rtu_hdr = (void *)rtu_port_shmem + rtu_port_shmem->data_off;
htab_shm = wrs_shm_follow(rtu_port_shmem, rtu_hdr->filters);
if (!htab_shm)
return -2;
/* Read data, with the sequential lock to have all data consistent */
while (1) {
ii = wrs_shm_seqbegin(rtu_port_shmem);
memcpy(&rtu_htab_local, htab_shm,
RTU_BUCKETS * HTAB_ENTRIES * sizeof(*htab_shm));
retries++;
if (retries > 100)
return -1;
if (!wrs_shm_seqretry(rtu_port_shmem, ii))
break; /* consistent read */
usleep(1000);
}
rtudexp_fd_entry_t fd_list[RTU_MAX_ENTRIES];
rtudexp_vd_entry_t vd_list[NUM_VLANS];
/* Convert hash table to ordered table. Table will be qsorted later,
* no need to qsort entire table */
*read_entries = 0;
empty = rtu_htab_local;
for (ii = 0; ii < RTU_BUCKETS * HTAB_ENTRIES; ii++) {
if (rtu_htab_local[ii].valid) {
memcpy(empty, &rtu_htab_local[ii], sizeof(*htab_shm));
empty++;
(*read_entries)++;
}
}
int n_fd_entries, n_vd_entries;
return 0;
}
int open_rtu_shm(void)
{
/* open rtu shm */
rtu_port_shmem = wrs_shm_get(wrs_shm_rtu, "", WRS_SHM_READ);
if (!rtu_port_shmem) {
printf("rtu_stat: %s: Can't join shmem: %s\n", __func__,
strerror(errno));
return -1;
}
/* check rtu shm version */
if (rtu_port_shmem->version != RTU_SHMEM_VERSION) {
printf("dump rtu: unknown version %i (known is %i)\n",
rtu_port_shmem->version, RTU_SHMEM_VERSION);
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
int i, isok;
int nports;
int htab_read_entries;
int vid_active = 0;
char mac_buf[ETH_ALEN_STR];
nports = get_nports_from_hal();
rtud_ch = minipc_client_create("rtud", 0);
if(!rtud_ch)
if (!rtud_ch)
{
printf("Can't connect to RTUd mini-rpc server\n");
return -1;
}
minipc_set_logfile(rtud_ch,stderr);
/* Open rtud's shmem */
if (open_rtu_shm()) {
printf("Can't open RTUd shmem\n");
return -1;
}
isok=0;
if(argc>1)
{
......@@ -247,52 +301,77 @@ int main(int argc, char **argv)
}
fetch_rtu_fd(fd_list, &n_fd_entries);
/* read filter entires from shm to local memory for data consistency */
if (read_htab(&htab_read_entries)) {
printf("Too many retries while reading htab entries from RTUd "
"shmem\n");
return -1;
}
qsort(fd_list, n_fd_entries, sizeof(rtudexp_fd_entry_t), cmp_entries);
qsort(rtu_htab_local, htab_read_entries,
sizeof(struct rtu_filtering_entry), cmp_rtu_entries);
printf("RTU Filtering Database Dump: %d rules\n", n_fd_entries);
printf("RTU Filtering Database Dump: %d rules\n", htab_read_entries);
printf("\n");
printf("MAC Dst.ports FID Type Age [s]\n");
printf("----------------------------------------------------------------------------------\n");
char mac_buf[ETH_ALEN_STR];
for(i=0;i<n_fd_entries;i++)
for (i = 0; i < htab_read_entries; i++)
{
if (!rtu_htab_local[i].valid)
continue;
printf("%-25s %-12s %2d %s (hash %03x:%x) ",
mac_to_buffer(fd_list[i].mac,mac_buf),
decode_ports(fd_list[i].dpm, nports),
fd_list[i].fid,
fd_list[i].dynamic ? "DYNAMIC":"STATIC ",
fd_list[i].hash,
fd_list[i].bucket);
if(fd_list[i].dynamic)
printf("%d\n", fd_list[i].age);
mac_to_buffer(rtu_htab_local[i].mac, mac_buf),
decode_ports(rtu_htab_local[i].port_mask_dst, nports),
rtu_htab_local[i].fid,
rtu_htab_local[i].dynamic ? "DYNAMIC" : "STATIC ",
rtu_htab_local[i].addr.hash,
rtu_htab_local[i].addr.bucket);
if (rtu_htab_local[i].dynamic)
printf("%d\n", rtu_htab_local[i].age);
else
printf("-\n");
}
printf("\n");
fetch_rtu_vd(vd_list, &n_vd_entries);
/* read vlans from shm to local memory for data consistency */
if (read_vlans()) {
printf("Too many retries while reading vlans from RTUd "
"shmem\n");
return -1;
}
printf("RTU VLAN Table Dump: %d active VIDs defined\n", n_vd_entries);
printf("RTU VLAN Table Dump:\n");
printf("\n");
printf(" VID FID MASK DROP PRIO PRIO_OVERRIDE\n");
printf("-----------------------------------------------------------\n");
for(i=0;i<n_vd_entries;i++)
{
printf("%4d %4d 0x%8x ", vd_list[i].vid, vd_list[i].fid, vd_list[i].port_mask);
if(vd_list[i].drop == 0) printf("NO ");
else printf("YES");
if(vd_list[i].has_prio == 0) printf(" -- ");
else printf(" %1d ",vd_list[i].prio);
if(vd_list[i].prio_override == 0) printf(" NO ");
else printf(" YES ");
for (i = 0; i < NUM_VLANS; i++) {
if ((vlan_tab_local[i].drop != 0)
&& (vlan_tab_local[i].port_mask == 0x0))
continue;
printf("%4d %4d 0x%8x ", i, vlan_tab_local[i].fid,
vlan_tab_local[i].port_mask);
if (vlan_tab_local[i].drop == 0)
printf("NO ");
else
printf("YES");
if (vlan_tab_local[i].has_prio == 0)
printf(" -- ");
else
printf(" %1d ", vlan_tab_local[i].prio);
if (vlan_tab_local[i].prio_override == 0)
printf(" NO ");
else
printf(" YES ");
printf("\n");
vid_active++;
}
printf("\n");
printf("%d active VIDs defined\n", vid_active);
printf("\n");
return 0;
}
......@@ -3,7 +3,7 @@
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <ppsi/ppsi.h>
#include <libwr/shmem.h>
#include <libwr/hal_shmem.h>
#include <minipc.h>
......@@ -15,7 +15,6 @@
#define SHOW_GUI 0
#define SHOW_STATS 1
#define SHOW_SNMP_GLOBALS 3
int mode = SHOW_GUI;
......@@ -26,25 +25,57 @@ static struct hal_port_state *hal_ports;
/* local copy of port state */
static struct hal_port_state hal_ports_local_copy[HAL_MAX_PORTS];
static int hal_nports_local;
static struct wrs_shm_head *ppsi_head;
static struct pp_globals *ppg;
static struct wr_servo_state_t *ppsi_servo;
static struct wr_servo_state_t ppsi_servo_local; /* local copy of
servo status */
static struct hal_temp_sensors *temp_sensors;
static struct hal_temp_sensors temp_sensors_local;
int read_ports(void){
int read_hal(void){
unsigned ii;
unsigned retries = 0;
/* read data, with the sequential lock to have all data consistent */
do {
while (1) {
ii = wrs_shm_seqbegin(hal_head);
memcpy(hal_ports_local_copy, hal_ports,
hal_nports_local*sizeof(struct hal_port_state));
memcpy(&temp_sensors_local, temp_sensors,
sizeof(*temp_sensors));
retries++;
if (retries > 100)
return -1;
if (!wrs_shm_seqretry(hal_head, ii))
break; /* consistent read */
usleep(1000);
}
return 0;
}
int read_servo(void){
unsigned ii;
unsigned retries = 0;
/* read data, with the sequential lock to have all data consistent */
while (1) {
ii = wrs_shm_seqbegin(ppsi_head);
memcpy(&ppsi_servo_local, ppsi_servo, sizeof(*ppsi_servo));
retries++;
if (retries > 100)
return -1;
if (!wrs_shm_seqretry(ppsi_head, ii))
break; /* consistent read */
usleep(1000);
} while (wrs_shm_seqretry(hal_head, ii));
}
return 0;
}
void init_shm(void)
{
struct hal_shmem_header *h;
......@@ -54,18 +85,52 @@ void init_shm(void)
fprintf(stderr, "unable to open shm for HAL!\n");
exit(-1);
}
/* check hal's shm version */
if (hal_head->version != HAL_SHMEM_VERSION) {
fprintf(stderr, "wr_mon: unknown HAL's shm version %i "
"(known is %i)\n",
hal_head->version, HAL_SHMEM_VERSION);
exit(-1);
}
h = (void *)hal_head + hal_head->data_off;
/* Assume number of ports does not change in runtime */
hal_nports_local = h->nports;
/* Even after HAL restart, HAL will place structures at the same
* addresses. No need to re-dereference pointer at each read. */
hal_ports = wrs_shm_follow(hal_head, h->ports);
if (hal_nports_local > HAL_MAX_PORTS) {
fprintf(stderr, "Too many ports reported by HAL. "
"%d vs %d supported\n",
hal_nports_local, HAL_MAX_PORTS);
exit(-1);
}
/* Even after HAL restart, HAL will place structures at the same
* addresses. No need to re-dereference pointer at each read. */
hal_ports = wrs_shm_follow(hal_head, h->ports);
if (!hal_ports) {
fprintf(stderr, "Unalbe to follow hal_ports pointer in HAL's "
"shmem");
exit(-1);
}
temp_sensors = &(h->temp);
ppsi_head = wrs_shm_get(wrs_shm_ptp, "", WRS_SHM_READ);
if (!ppsi_head) {
fprintf(stderr, "unable to open shm for PPSI!\n");
exit(-1);
}
/* check hal's shm version */
if (ppsi_head->version != WRS_PPSI_SHMEM_VERSION) {
fprintf(stderr, "wr_mon: unknown PPSI's shm version %i "
"(known is %i)\n",
ppsi_head->version, WRS_PPSI_SHMEM_VERSION);
exit(-1);
}
ppg = (void *)ppsi_head + ppsi_head->data_off;
ppsi_servo = wrs_shm_follow(ppsi_head, ppg->global_ext_data);
if (!ppsi_servo) {
fprintf(stderr, "Cannot follow ppsi_servo in shmem.\n");
exit(-1);
}
ptp_ch = minipc_client_create("ptpd", 0);
if (!ptp_ch)
......@@ -118,6 +183,15 @@ void show_ports(void)
case HEXP_PORT_MODE_WR_SLAVE:
term_cprintf(C_WHITE, "WR Slave ");
break;
case HEXP_PORT_MODE_NON_WR:
term_cprintf(C_WHITE, "Non WR ");
break;
case HEXP_PORT_MODE_WR_M_AND_S:
term_cprintf(C_WHITE, "WR auto ");
break;
default:
term_cprintf(C_WHITE, "Unknown ");
break;
}
if (port_state->locked)
......@@ -145,9 +219,23 @@ void show_ports(void)
printf("port:%s ", if_name);
printf("lnk:%d ", state_up(port_state->state));
printf("mode:%s ",
port_state->mode == HEXP_PORT_MODE_WR_SLAVE
? "S" : "M");
switch (port_state->mode) {
case HEXP_PORT_MODE_WR_MASTER:
printf("mode:M ");
break;
case HEXP_PORT_MODE_WR_SLAVE:
printf("mode:S ");
break;
case HEXP_PORT_MODE_NON_WR:
printf("mode:N ");
break;
case HEXP_PORT_MODE_WR_M_AND_S:
printf("mode:A ");
break;
default:
printf("mode:U ");
break;
}
printf("lock:%d ", port_state->locked);
}
printf("\n");
......@@ -190,101 +278,136 @@ static void show_unadorned_ports(void)
void show_servo(void)
{
ptpdexp_sync_state_t ss;
int64_t total_asymmetry;
int64_t crtt;
minipc_call(ptp_ch, 2000, &__rpcdef_get_sync_state, &ss);
total_asymmetry = ppsi_servo_local.picos_mu -
2LL * ppsi_servo_local.delta_ms;
crtt = ppsi_servo_local.picos_mu - ppsi_servo_local.delta_tx_m -
ppsi_servo_local.delta_rx_m - ppsi_servo_local.delta_tx_s -
ppsi_servo_local.delta_rx_s;
if(mode == SHOW_GUI) {
term_cprintf(C_BLUE, "Synchronization status:\n");
if(!ss.valid)
{
if (!ppsi_servo_local.valid) {
term_cprintf(C_RED, "Master mode or sync info not valid\n");
return;
}
term_cprintf(C_GREY, "Servo state: ");
term_cprintf(C_WHITE, "%s\n", ss.slave_servo_state);
term_cprintf(C_WHITE, "%s\n",
ppsi_servo_local.servo_state_name);
term_cprintf(C_GREY, "Phase tracking: ");
if(ss.tracking_enabled)
if (ppsi_servo_local.tracking_enabled)
term_cprintf(C_GREEN, "ON\n");
else
term_cprintf(C_RED,"OFF\n");
term_cprintf(C_RED, "OFF\n");
term_cprintf(C_GREY, "Synchronization source: ");
term_cprintf(C_WHITE, "%s\n", ss.sync_source);
/* not implemented */
/*term_cprintf(C_GREY, "Synchronization source: ");
term_cprintf(C_WHITE, "%s\n", ss.sync_source);*/
term_cprintf(C_BLUE, "\nTiming parameters:\n\n");
term_cprintf(C_GREY, "Round-trip time (mu): ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.mu/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n",
ppsi_servo_local.picos_mu/1000.0);
term_cprintf(C_GREY, "Master-slave delay: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.delay_ms/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n",
ppsi_servo_local.delta_ms/1000.0);
term_cprintf(C_GREY, "Link length: ");
term_cprintf(C_WHITE, "%.0f meters \n",
ss.delay_ms/1e12 * 300e6 / 1.55);
term_cprintf(C_WHITE, "%.0f meters\n",
ppsi_servo_local.delta_ms/1e12 * 300e6 / 1.55);
term_cprintf(C_GREY, "Master PHY delays: ");
term_cprintf(C_WHITE, "TX: %.3f nsec, RX: %.3f nsec\n",
ss.delta_tx_m/1000.0, ss.delta_rx_m/1000.0);
ppsi_servo_local.delta_tx_m/1000.0,
ppsi_servo_local.delta_rx_m/1000.0);
term_cprintf(C_GREY, "Slave PHY delays: ");
term_cprintf(C_WHITE, "TX: %.3f nsec, RX: %.3f nsec\n",
ss.delta_tx_s/1000.0, ss.delta_rx_s/1000.0);
ppsi_servo_local.delta_tx_s/1000.0,
ppsi_servo_local.delta_rx_s/1000.0);
term_cprintf(C_GREY, "Total link asymmetry: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.total_asymmetry/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n", total_asymmetry/1000.0);
if (0) {
/*if (0) {
term_cprintf(C_GREY, "Fiber asymmetry: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.fiber_asymmetry/1000.0);
}
}*/
term_cprintf(C_GREY, "Clock offset: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.cur_offset/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n",
ppsi_servo_local.offset/1000.0);
term_cprintf(C_GREY, "Phase setpoint: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.cur_setpoint/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n",
ppsi_servo_local.cur_setpoint/1000.0);
term_cprintf(C_GREY, "Skew: ");
term_cprintf(C_WHITE, "%.3f nsec\n", ss.cur_skew/1000.0);
term_cprintf(C_WHITE, "%.3f nsec\n",
ppsi_servo_local.skew/1000.0);
term_cprintf(C_GREY, "Servo update counter: ");
term_cprintf(C_WHITE, "%lld times\n", ss.update_count);
term_cprintf(C_WHITE, "%u times\n",
ppsi_servo_local.update_count);
}
else if(mode == SHOW_STATS) {
printf("SERVO ");
printf("sv:%d ", ss.valid ? 1:0);
printf("ss:'%s' ", ss.slave_servo_state);
printf("mu:%llu ", ss.mu);
printf("dms:%llu ", ss.delay_ms);
printf("dtxm:%llu drxm:%llu ", ss.delta_tx_m, ss.delta_rx_m);
printf("dtxs:%llu drxs:%llu ", ss.delta_tx_s, ss.delta_rx_s);
printf("asym:%lld ", ss.total_asymmetry);
printf("crtt:%llu ", ss.mu - ss.delta_tx_m - ss.delta_rx_m -
ss.delta_tx_s - ss.delta_rx_s);
printf("cko:%lld ", ss.cur_offset);
printf("setp:%lld ", ss.cur_setpoint);
printf("ucnt:%llu ", ss.update_count);
printf("sv:%d ", ppsi_servo_local.valid ? 1 : 0);
printf("ss:'%s' ", ppsi_servo_local.servo_state_name);
printf("mu:%llu ", ppsi_servo_local.picos_mu);
printf("dms:%llu ", ppsi_servo_local.delta_ms);
printf("dtxm:%d drxm:%d ", ppsi_servo_local.delta_tx_m,
ppsi_servo_local.delta_rx_m);
printf("dtxs:%d drxs:%d ", ppsi_servo_local.delta_tx_s,
ppsi_servo_local.delta_rx_s);
printf("asym:%lld ", total_asymmetry);
printf("crtt:%llu ", crtt);
printf("cko:%lld ", ppsi_servo_local.offset);
printf("setp:%d ", ppsi_servo_local.cur_setpoint);
printf("ucnt:%u ", ppsi_servo_local.update_count);
printf("\n");
} else if (mode == SHOW_SNMP_GLOBALS) {
if(!ss.valid)
return;
/* This is oh so similar to the above, but by lines */
printf("gm_id: f0:f0:f0:f0:f0:f0:f0:f0\n"); /* FIXME */
printf("clock_id: f1:f1:f1:f1:f1:f1:f1:f1\n"); /* FIXME */
printf("mode: 9999\n"); /* FIXME */
printf("servo_state: %s\n", ss.slave_servo_state);
printf("tracking: %i\n", ss.tracking_enabled ? 1 : 0);
printf("source: %s\n", ss.sync_source);
printf("ck_offset: %lli\n", ss.cur_offset);
printf("skew: %li\n", (long)ss.cur_skew);
printf("rtt: %lli\n", ss.mu);
printf("llength: %li\n", (long)(ss.delay_ms/1e12 * 300e6 / 1.55));
printf("servo_upd: %lli\n", ss.update_count);
}
}
void show_temperatures(void)
{
if (mode == SHOW_GUI) {
term_cprintf(C_BLUE, "\nTemperatures:\n");
term_cprintf(C_GREY, "FPGA: ");
term_cprintf(C_WHITE, "%2.2f ",
temp_sensors_local.fpga/256.0);
term_cprintf(C_GREY, "PLL: ");
term_cprintf(C_WHITE, "%2.2f ",
temp_sensors_local.pll/256.0);
term_cprintf(C_GREY, "PSL: ");
term_cprintf(C_WHITE, "%2.2f ",
temp_sensors_local.psl/256.0);
term_cprintf(C_GREY, "PSR: ");
term_cprintf(C_WHITE, "%2.2f\n",
temp_sensors_local.psr/256.0);
term_cprintf(C_BLUE, "Temperature thresholds:\n");
term_cprintf(C_GREY, "FPGA: ");
term_cprintf(C_WHITE, "%5d ",
temp_sensors_local.fpga_thold);
term_cprintf(C_GREY, "PLL: ");
term_cprintf(C_WHITE, "%5d ",
temp_sensors_local.pll_thold);
term_cprintf(C_GREY, "PSL: ");
term_cprintf(C_WHITE, "%5d ",
temp_sensors_local.psl_thold);
term_cprintf(C_GREY, "PSR: ");
term_cprintf(C_WHITE, "%5d\n",
temp_sensors_local.psr_thold);
}
}
......@@ -299,6 +422,7 @@ void show_all()
}
show_ports();
show_servo();
show_temperatures();
fflush(stdout);
}
......@@ -317,13 +441,8 @@ int main(int argc, char *argv[])
case 'b':
usecolor = 0;
break;
case 'g':
mode = SHOW_SNMP_GLOBALS;
read_ports();
show_all();
exit(0);
case 'w': /* for the web interface */
read_ports();
read_hal();
show_unadorned_ports();
exit(0);
default:
......@@ -351,7 +470,8 @@ int main(int argc, char *argv[])
track_onoff);
}
}
read_ports();
read_hal();
read_servo();
show_all();
/* If we got broken pipe or anything, exit */
if (ferror(stdout))
......
......@@ -67,6 +67,13 @@ int hal_shm_init(void)
"FATAL: wr_phytool unable to open shm to HAL.\n");
return -1;
}
/* check hal's shm version */
if (hal_head->version != HAL_SHMEM_VERSION) {
fprintf(stderr, "wr_mon: unknown hal's shm version %i "
"(known is %i)\n",
hal_head->version, HAL_SHMEM_VERSION);
return -1;
}
h = (void *)hal_head + hal_head->data_off;
......
......@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <libwr/shmem.h>
......@@ -11,6 +12,7 @@
#include <ppsi/ppsi.h>
#include <ppsi-wrs.h>
/* be safe, in case some other header had them slightly differently */
#undef container_of
#undef offsetof
......@@ -23,7 +25,6 @@
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
char *name_id_to_name[WRS_SHM_N_NAMES] = {
[wrs_shm_ptp] = "ptpd/ppsi",
[wrs_shm_rtu] = "wrsw_rtud",
......@@ -45,8 +46,11 @@ enum dump_type {
dump_type_unsigned_char,
dump_type_unsigned_short,
dump_type_double,
dump_type_float,
dump_type_pointer,
/* and strange ones, from IEEE */
dump_type_UInteger64,
dump_type_Integer64,
dump_type_UInteger32,
dump_type_Integer32,
dump_type_UInteger16,
......@@ -61,8 +65,14 @@ enum dump_type {
/* and this is ours */
dump_type_TimeInternal,
dump_type_ip_address,
dump_type_sfp_flags,
dump_type_port_mode,
dump_type_sensor_temp,
};
static int dump_all_rtu_entries = 0; /* rtu exports 4096 vlans and 2048 htab
entries */
/*
* A structure to dump fields. This is meant to simplify things, see use here
*/
......@@ -93,6 +103,12 @@ void dump_one_field(void *addr, struct dump_info *info)
printf("%02x%c", ((unsigned char *)p)[i],
i == info->size - 1 ? '\n' : ':');
break;
case dump_type_UInteger64:
printf("%lld\n", *(unsigned long long *)p);
break;
case dump_type_Integer64:
printf("%lld\n", *(long long *)p);
break;
case dump_type_uint32_t:
printf("0x%08lx\n", (long)*(uint32_t *)p);
break;
......@@ -118,13 +134,15 @@ void dump_one_field(void *addr, struct dump_info *info)
case dump_type_double:
printf("%lf\n", *(double *)p);
break;
case dump_type_float:
printf("%f\n", *(float *)p);
break;
case dump_type_pointer:
printf("%p\n", *(void **)p);
break;
case dump_type_Integer16:
printf("%i\n", *(short *)p);
break;
case dump_type_TimeInternal:
printf("correct %i: %10i.%09i:%04i\n", ti->correct,
ti->seconds, ti->nanoseconds, ti->phase);
......@@ -154,12 +172,49 @@ void dump_one_field(void *addr, struct dump_info *info)
cq->clockClass, cq->clockAccuracy, cq->clockAccuracy,
cq->offsetScaledLogVariance);
break;
case dump_type_sfp_flags:
if (*(uint32_t *)p & SFP_FLAG_CLASS_DATA)
printf("SFP class data, ");
if (*(uint32_t *)p & SFP_FLAG_DEVICE_DATA)
printf("SFP device data, ");
if (*(uint32_t *)p & SFP_FLAG_1GbE)
printf("SFP is 1GbE, ");
if (*(uint32_t *)p & SFP_FLAG_IN_DB)
printf("SFP in data base, ");
printf("\n");
break;
case dump_type_port_mode:
switch (*(uint32_t *)p) {
case HEXP_PORT_MODE_WR_MASTER:
printf("WR Master\n");
break;
case HEXP_PORT_MODE_WR_SLAVE:
printf("WR Slave\n");
break;
case HEXP_PORT_MODE_NON_WR:
printf("Non-WR\n");
break;
case HEXP_PORT_MODE_WR_M_AND_S:
printf("Auto\n");
break;
default:
printf("Undefined\n");
break;
}
break;
case dump_type_sensor_temp:
printf("%f\n", ((float)(*(int *)p >> 4)) / 16.0);
break;
}
}
void dump_many_fields(void *addr, struct dump_info *info, int ninfo)
{
int i;
if (!addr) {
fprintf(stderr, "dump: pointer not valid\n");
return;
}
for (i = 0; i < ninfo; i++)
dump_one_field(addr, info + i);
}
......@@ -177,6 +232,20 @@ void dump_many_fields(void *addr, struct dump_info *info, int ninfo)
.size = _size, \
}
#undef DUMP_STRUCT
#define DUMP_STRUCT struct hal_shmem_header
struct dump_info hal_shmem_info [] = {
DUMP_FIELD(int, nports),
DUMP_FIELD(sensor_temp, temp.fpga),
DUMP_FIELD(sensor_temp, temp.pll),
DUMP_FIELD(sensor_temp, temp.psl),
DUMP_FIELD(sensor_temp, temp.psr),
DUMP_FIELD(int, temp.fpga_thold),
DUMP_FIELD(int, temp.pll_thold),
DUMP_FIELD(int, temp.psl_thold),
DUMP_FIELD(int, temp.psr_thold),
};
/* map for fields of hal_port_state (hal_shmem.h) */
#undef DUMP_STRUCT
#define DUMP_STRUCT struct hal_port_state
......@@ -187,7 +256,7 @@ struct dump_info hal_port_info [] = {
DUMP_FIELD(int, hw_index),
DUMP_FIELD(int, fd),
DUMP_FIELD(int, hw_addr_auto),
DUMP_FIELD(int, mode),
DUMP_FIELD(port_mode, mode),
DUMP_FIELD(int, state),
DUMP_FIELD(int, fiber_index),
DUMP_FIELD(int, locked),
......@@ -202,7 +271,8 @@ struct dump_info hal_port_info [] = {
DUMP_FIELD(int, calib.tx_calibrated),
/* Another internal structure, with a final pointer */
DUMP_FIELD(int, calib.sfp.flags),
DUMP_FIELD(sfp_flags, calib.sfp.flags),
DUMP_FIELD_SIZE(char, calib.sfp.vendor_name, 16),
DUMP_FIELD_SIZE(char, calib.sfp.part_num, 16),
DUMP_FIELD_SIZE(char, calib.sfp.vendor_serial, 16),
DUMP_FIELD(double, calib.sfp.alpha),
......@@ -235,6 +305,10 @@ int dump_hal_mem(struct wrs_shm_head *head)
return -1;
}
h = (void *)head + head->data_off;
/* dump hal's shmem */
dump_many_fields(h, hal_shmem_info, ARRAY_SIZE(hal_shmem_info));
n = h->nports;
p = wrs_shm_follow(head, h->ports);
......@@ -293,6 +367,7 @@ int dump_rtu_mem(struct wrs_shm_head *head)
{
struct rtu_shmem_header *rtu_h;
struct rtu_filtering_entry *rtu_filters;
struct rtu_filtering_entry *rtu_filters_cur;
struct rtu_vlan_table_entry *rtu_vlans;
int i, j;
......@@ -312,16 +387,24 @@ int dump_rtu_mem(struct wrs_shm_head *head)
for (i = 0; i < HTAB_ENTRIES; i++) {
for (j = 0; j < RTU_BUCKETS; j++) {
rtu_filters_cur = rtu_filters + i*RTU_BUCKETS + j;
if ((!dump_all_rtu_entries)
&& (!rtu_filters_cur->valid))
/* don't display empty entries */
continue;
printf("dump htab[%d][%d]\n", i, j);
dump_many_fields(rtu_filters+i*RTU_BUCKETS+j,
htab_info, ARRAY_SIZE(htab_info));
dump_many_fields(rtu_filters_cur, htab_info,
ARRAY_SIZE(htab_info));
}
}
for (i = 0; i < NUM_VLANS; i++) {
for (i = 0; i < NUM_VLANS; i++, rtu_vlans++) {
if ((!dump_all_rtu_entries) && (rtu_vlans->drop != 0
&& rtu_vlans->port_mask == 0x0))
/* don't display empty entries */
continue;
printf("dump vlan %i\n", i);
dump_many_fields(rtu_vlans, vlan_info, ARRAY_SIZE(vlan_info));
rtu_vlans++;
}
return 0;
}
......@@ -345,6 +428,7 @@ struct dump_info ppg_info [] = {
DUMP_FIELD(int, rxdrop),
DUMP_FIELD(int, txdrop),
DUMP_FIELD(pointer, arch_data),
DUMP_FIELD(pointer, global_ext_data),
};
#undef DUMP_STRUCT
......@@ -394,6 +478,38 @@ struct dump_info dstp_info [] = {
DUMP_FIELD(Enumeration8, timeSource),
};
#undef DUMP_STRUCT
#define DUMP_STRUCT struct wr_servo_state_t /* Horrible typedef */
struct dump_info servo_state_info [] = {
DUMP_FIELD_SIZE(char, if_name, 16),
DUMP_FIELD(int, state),
DUMP_FIELD_SIZE(char, servo_state_name, 32),
DUMP_FIELD(int, next_state),
DUMP_FIELD(TimeInternal, mu), /* half of the RTT */
DUMP_FIELD(Integer64, picos_mu),
DUMP_FIELD(Integer32, delta_tx_m),
DUMP_FIELD(Integer32, delta_rx_m),
DUMP_FIELD(Integer32, delta_tx_s),
DUMP_FIELD(Integer32, delta_rx_s),
DUMP_FIELD(Integer32, cur_setpoint),
DUMP_FIELD(Integer32, delta_ms),
DUMP_FIELD(Integer32, delta_ms_prev),
DUMP_FIELD(TimeInternal, t1),
DUMP_FIELD(TimeInternal, t2),
DUMP_FIELD(TimeInternal, t3),
DUMP_FIELD(TimeInternal, t4),
DUMP_FIELD(UInteger64, last_tics),
DUMP_FIELD(Integer32, fiber_fix_alpha),
DUMP_FIELD(Integer32, clock_period_ps),
DUMP_FIELD(int, missed_iters),
DUMP_FIELD(int, valid),
DUMP_FIELD(UInteger32, update_count),
DUMP_FIELD(int, tracking_enabled),
DUMP_FIELD(Integer64, skew),
DUMP_FIELD(Integer64, offset),
};
#undef DUMP_STRUCT
#define DUMP_STRUCT struct pp_instance
struct dump_info ppi_info [] = {
......@@ -468,6 +584,7 @@ int dump_ppsi_mem(struct wrs_shm_head *head)
DSCurrent *dsc;
DSParent *dsp;
DSTimeProperties *dstp;
struct wr_servo_state_t *global_ext_data;
int i;
if (head->version != WRS_PPSI_SHMEM_VERSION) {
......@@ -495,6 +612,11 @@ int dump_ppsi_mem(struct wrs_shm_head *head)
printf("time properties data set:\n");
dump_many_fields(dstp, dstp_info, ARRAY_SIZE(dstp_info));
global_ext_data = wrs_shm_follow(head, ppg->global_ext_data);
printf("global external data set:\n");
dump_many_fields(global_ext_data, servo_state_info,
ARRAY_SIZE(servo_state_info));
ppi = wrs_shm_follow(head, ppg->pp_instances);
for (i = 0; i < ppg->nlinks; i++) {
printf("ppsi instance %i:\n", i);
......@@ -538,13 +660,37 @@ dump_f *name_id_to_f[WRS_SHM_N_NAMES] = {
[wrs_shm_rtu] = dump_rtu_mem,
};
void print_info(char *prgname)
{
printf("usage: %s [parameters]\n", prgname);
printf(""
" Dump shmem\n"
" -a Dump all rtu entries. By default only valid\n"
" entries are printed. Note there are 2048 htab\n"
" and 4096 vlan entries!\n"
" -h Show this message\n");
}
int main(int argc, char **argv)
{
struct wrs_shm_head *head;
dump_f *f;
void *m;
int i;
int c;
while ((c = getopt(argc, argv, "ah")) != -1) {
switch (c) {
case 'a':
dump_all_rtu_entries = 1;
break;
case 'h':
default:
print_info(argv[0]);
exit(1);
}
}
for (i = 0; i < WRS_SHM_N_NAMES; i++) {
m = wrs_shm_get(i, "reader", 0);
if (!m) {
......
......@@ -34,15 +34,16 @@
#include "fpga_io.h"
#include "wrs_vlans.h"
#include <libwr/shmem.h>
#include <libwr/rtu_shmem.h>
int debug = 0;
struct minipc_ch *rtud_ch;
struct rtu_vlans_t *rtu_vlans = NULL;
char *prgname;
static int debug = 0;
static struct minipc_ch *rtud_ch;
static struct rtu_vlans_t *rtu_vlans = NULL;
static char *prgname;
/* runtime options */
struct option ropts[] = {
static struct option ropts[] = {
{"help", 0, NULL, OPT_HELP},
{"debug", 0, &debug, 1},
{"clear", 0, NULL, OPT_CLEAR},
......@@ -62,9 +63,25 @@ struct option ropts[] = {
{0,}};
/*******************/
struct s_port_vlans vlans[NPORTS];
static struct s_port_vlans vlans[NPORTS];
static unsigned long portmask;
static int print_help();
static void print_config(struct s_port_vlans *vlans);
static int apply_settings(struct s_port_vlans *vlans);
static int clear_all();
static int set_rtu_vlan(int vid, int fid, int pmask, int drop, int prio,
int del, int flags);
static void free_rtu_vlans(struct rtu_vlans_t *ptr);
static void list_rtu_vlans(void);
static void list_ep_vlans(void);
static int rtu_find_vlan(struct rtu_vlan_table_entry *rtu_vlan_entry, int vid,
int fid);
static int config_rtud(void);
unsigned long portmask;
struct rtu_vlan_table_entry *vlan_tab_shm;
struct wrs_shm_head *rtu_port_shmem;
static inline int nextport(int i, unsigned long pmask) /* helper for for_each_port() below */
{
......@@ -117,6 +134,7 @@ int main(int argc, char *argv[])
{
int c, i, arg;
unsigned long conf_pmask = 0; //current '--ep' port mask
struct rtu_shmem_header *rtu_hdr;
prgname = argv[0];
......@@ -137,6 +155,31 @@ int main(int argc, char *argv[])
exit(1);
}
/* open rtu shm */
rtu_port_shmem = wrs_shm_get(wrs_shm_rtu, "", WRS_SHM_READ);
if (!rtu_port_shmem) {
fprintf(stderr, "%s: Can't join RTU's shmem\n",
prgname);
exit(1);
}
/* FIXME: Wait for rtud to fill shmem */
/* check rtu shm version */
if (rtu_port_shmem->version != RTU_SHMEM_VERSION) {
fprintf(stderr, "%s: unknown version %i (known is %i)\n",
prgname, rtu_port_shmem->version, RTU_SHMEM_VERSION);
exit(1);
}
/* get vlans array */
rtu_hdr = (void *)rtu_port_shmem + rtu_port_shmem->data_off;
vlan_tab_shm = wrs_shm_follow(rtu_port_shmem, rtu_hdr->vlans);
if (!vlan_tab_shm) {
fprintf(stderr, "%s: cannot follow pointer to vlans in "
"RTU's shmem\n", prgname);
exit(1);
}
if (shw_fpga_mmap_init() < 0) {
fprintf(stderr, "%s: Can't access device memory\n", prgname);
exit(1);
......@@ -257,7 +300,7 @@ int main(int argc, char *argv[])
return 0;
}
int print_help(char *prgname)
static int print_help(char *prgname)
{
fprintf(stderr, "Use: %s [--ep <port number> <EP options> --ep <port number> "
"<EP options> ...] [--rvid <vid> --rfid <fid> --rmask <mask> --rdrop "
......@@ -289,7 +332,7 @@ int print_help(char *prgname)
return 0;
}
void print_config(struct s_port_vlans *vlans)
static void print_config(struct s_port_vlans *vlans)
{
int i;
......@@ -313,7 +356,7 @@ static void ep_write(int ep, int offset, uint32_t value)
_fpga_writel(0x30000 + ep * 0x400 + offset, value);
}
int apply_settings(struct s_port_vlans *vlans)
static int apply_settings(struct s_port_vlans *vlans)
{
int ep;
uint32_t v, r;
......@@ -349,67 +392,96 @@ int apply_settings(struct s_port_vlans *vlans)
return 0;
}
int config_rtud(void)
static int config_rtud(void)
{
struct rtu_vlans_t *cur;
struct rtu_vlans_t *old_list, *old_entry;
struct rtu_vlan_table_entry rtu_vlan_entry;
int ret, val;
old_list = rtu_retrieve_config();
cur = rtu_vlans;
while(cur) {
old_entry = rtu_find_vlan(old_list, cur->vid, cur->flags & VALID_FID?cur->fid:-1);
if (rtu_find_vlan(&rtu_vlan_entry, cur->vid,
(cur->flags & VALID_FID) ? cur->fid : -1)) {
/*preserve previous settings if not overwritten*/
if(old_entry!=NULL && !(cur->flags & VALID_FID))
cur->fid = old_entry->fid;
if(old_entry!=NULL && !(cur->flags & VALID_PMASK))
cur->pmask = old_entry->pmask;
if(old_entry!=NULL && !(cur->flags & VALID_DROP))
cur->drop = old_entry->drop;
if(old_entry!=NULL && !(cur->flags & VALID_PRIO)) {
cur->prio = old_entry->prio;
cur->has_prio = old_entry->has_prio;
cur->prio_override = old_entry->prio_override;
if (!(cur->flags & VALID_FID))
cur->fid = rtu_vlan_entry.fid;
if (!(cur->flags & VALID_PMASK))
cur->pmask = rtu_vlan_entry.port_mask;
if (!(cur->flags & VALID_DROP))
cur->drop = rtu_vlan_entry.drop;
if (!(cur->flags & VALID_PRIO)) {
cur->prio = rtu_vlan_entry.prio;
cur->has_prio = rtu_vlan_entry.has_prio;
cur->prio_override =
rtu_vlan_entry.prio_override;
}
}
ret = minipc_call(rtud_ch, MINIPC_TIMEOUT, &rtud_export_vlan_entry, &val,
cur->vid, cur->fid, cur->pmask, cur->drop, cur->prio, cur->has_prio,
cur->prio_override);
cur = cur->next;
}
free_rtu_vlans(old_list);
return 0;
}
void list_rtu_vlans(void)
static void list_rtu_vlans(void)
{
struct rtu_vlans_t *p = rtu_retrieve_config();
if (!p)
return;
unsigned ii;
unsigned retries = 0;
static struct rtu_vlan_table_entry vlan_tab_local[NUM_VLANS];
/* read data, with the sequential lock to have all data consistent */
while (1) {
ii = wrs_shm_seqbegin(rtu_port_shmem);
memcpy(&vlan_tab_local, vlan_tab_shm,
NUM_VLANS * sizeof(*vlan_tab_shm));
retries++;
if (retries > 100) {
fprintf(stderr, "%s: couldn't read consistent data "
"from RTU's shmem. Use inconsistent\n",
prgname);
break; /* use inconsistent data */
}
if (!wrs_shm_seqretry(rtu_port_shmem, ii))
break; /* consistent read */
usleep(1000);
}
printf("# VID FID MASK DROP PRIO PRIO_OVERRIDE\n");
printf("#----------------------------------------------------------\n");
while(p) {
printf("%4d %4d 0x%08x ", p->vid, p->fid, p->pmask);
if(p->drop == 0) printf("NO ");
else printf("YES");
if(p->has_prio == 0) printf(" -- ");
else printf(" %1d ", p->prio);
for (ii = 0; ii < NUM_VLANS; ii++) {
/* ignore entires that are not active */
if ((vlan_tab_local[ii].drop != 0)
&& (vlan_tab_local[ii].port_mask == 0x0))
continue;
printf("%4d %4d 0x%08x ", ii, vlan_tab_local[ii].fid,
vlan_tab_local[ii].port_mask);
if (vlan_tab_local[ii].drop == 0)
printf("NO ");
else
printf("YES");
if (vlan_tab_local[ii].has_prio == 0)
printf(" -- ");
else
printf(" %1d ", vlan_tab_local[ii].prio);
if (vlan_tab_local[ii].prio_override == 0)
printf(" NO ");
else
printf(" YES ");
if(p->prio_override == 0) printf(" NO ");
else printf(" YES ");
printf("\n");
p = p->next;
}
printf("\n");
}
void list_ep_vlans(void)
static void list_ep_vlans(void)
{
uint32_t v, r;
int ep;
......@@ -431,25 +503,23 @@ void list_ep_vlans(void)
return;
}
int clear_all()
static int clear_all()
{
struct rtu_vlans_t *p;
uint32_t r;
int val, i;
int ep;
/* cancel all rtu-administered vlans */
p= rtu_retrieve_config();
while (p) {
if(p->vid != 0)
minipc_call(rtud_ch, MINIPC_TIMEOUT,
&rtud_export_vlan_entry, &val, p->vid,
p->fid, 0, 1, 0, 0, 0);
else
minipc_call(rtud_ch, MINIPC_TIMEOUT, &rtud_export_vlan_entry,
&val, 0, 0, 0xffffffff, 0, 0, 0, 0);
for (i = 1; i < NUM_VLANS; i++) {
if ((vlan_tab_shm[i].drop != 0)
&& (vlan_tab_shm[i].port_mask == 0x0))
continue;
minipc_call(rtud_ch, MINIPC_TIMEOUT,
&rtud_export_vlan_entry, &val, 0,
0, 0xffffffff, 0, 0, 0, 0);
p = p->next;
&rtud_export_vlan_entry, &val, i,
vlan_tab_shm[i].fid, 0, 1, 0, 0, 0);
}
/* cancel tagging/untagging in all endpoints*/
......@@ -465,7 +535,8 @@ int clear_all()
return 0;
}
int set_rtu_vlan(int vid, int fid, int pmask, int drop, int prio, int del, int flags)
static int set_rtu_vlan(int vid, int fid, int pmask, int drop, int prio,
int del, int flags)
{
struct rtu_vlans_t *cur = rtu_vlans;;
......@@ -520,7 +591,7 @@ int set_rtu_vlan(int vid, int fid, int pmask, int drop, int prio, int del, int f
return 0;
}
void free_rtu_vlans(struct rtu_vlans_t *ptr)
static void free_rtu_vlans(struct rtu_vlans_t *ptr)
{
struct rtu_vlans_t *next = NULL;
......@@ -531,61 +602,36 @@ void free_rtu_vlans(struct rtu_vlans_t *ptr)
}
}
struct rtu_vlans_t* rtu_retrieve_config(void)
static int rtu_find_vlan(struct rtu_vlan_table_entry *rtu_vlan_entry, int vid,
int fid)
{
rtudexp_vd_list_t vlist;
rtudexp_vd_entry_t *ventry;
int idx = 0, i;
struct rtu_vlans_t *config = NULL, *cur = NULL, *ptr;
do {
if (minipc_call(rtud_ch, MINIPC_TIMEOUT,
&rtud_export_get_vd_list, &vlist, idx) < 0) {
/* Duplicated from above */
fprintf(stderr, "%s: minipc_call: %s\n", prgname, strerror(errno));
return config; /* maybe partly good */
}
for(i=0; i<vlist.num_entries; ++i) {
ptr = malloc(sizeof(struct rtu_vlans_t));
if (!ptr) {
fprintf(stderr, "%s: reading RTUd table: %s\n",
prgname, strerror(errno));
return config; /* may be partly good */
}
ventry = &vlist.list[i];
if(config == NULL) { /* first item */
config = cur = ptr;
} else {
cur->next = ptr;
cur = ptr;
}
cur->vid = ventry->vid;
cur->fid = ventry->fid;
cur->pmask = ventry->port_mask;
cur->drop = ventry->drop;
cur->prio = ventry->prio;
cur->has_prio = ventry->has_prio;
cur->prio_override = ventry->prio_override;
}
idx = vlist.next;
} while(idx>0);
return config;
}
struct rtu_vlans_t* rtu_find_vlan(struct rtu_vlans_t *conf, int vid, int fid)
{
struct rtu_vlans_t *cur;
cur = conf;
while(cur) {
if((cur->vid == vid && cur->fid == fid) || (cur->vid == vid && fid == -1))
break;
cur = cur->next;
}
unsigned ii;
unsigned retries = 0;
/* copy data no mater if it will be used later, with the sequential
* lock to have all data consistent */
while (1) {
ii = wrs_shm_seqbegin(rtu_port_shmem);
memcpy(rtu_vlan_entry, &vlan_tab_shm[vid],
sizeof(*rtu_vlan_entry));
retries++;
if (retries > 100) {
fprintf(stderr, "%s: couldn't read consistent "
"data from RTU's shmem. "
"Use inconsistent\n", prgname);
break; /* use inconsistent data */
}
if (!wrs_shm_seqretry(rtu_port_shmem, ii))
break; /* consistent read */
usleep(1000);
}
/* Ignore entires that are not active */
if ((rtu_vlan_entry->drop != 0)
&& (rtu_vlan_entry->port_mask == 0x0))
return 0;
return cur;
if ((fid == rtu_vlan_entry->fid) || (fid == -1))
return 1;
return 0;
}
......@@ -82,16 +82,4 @@ struct rtu_vlans_t
struct rtu_vlans_t *next;
};
int print_help();
void print_config(struct s_port_vlans *vlans);
int apply_settings(struct s_port_vlans *vlans);
int clear_all();
int set_rtu_vlan(int vid, int fid, int pmask, int drop, int prio, int del, int flags);
void free_rtu_vlans(struct rtu_vlans_t *ptr);
void list_rtu_vlans(void);
void list_ep_vlans(void);
struct rtu_vlans_t* rtu_retrieve_config(void);
struct rtu_vlans_t* rtu_find_vlan(struct rtu_vlans_t *conf, int vid, int fid);
int config_rtud(void);
#endif
......@@ -23,7 +23,7 @@ CFLAGS = -O -g -Wall \
-I$(LINUX)/arch/arm/mach-at91/include
LDFLAGS = -L../libwr -L../mini-rpc \
-lminipc -lm -ldl -lwr
-lm -ldl -lwr -lminipc
all: $(BINARY)
......
......@@ -2,6 +2,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <libwr/wrs-msg.h>
#include <libwr/pps_gen.h> /* for direct access to DMPLL and PPS generator */
......@@ -10,6 +13,7 @@
#include <rt_ipc.h>
#include <minipc.h>
#include <libwr/shmem.h>
#define HAL_EXPORT_STRUCTURES
#include <hal/hal_exports.h> /* for exported structs/function protos */
......@@ -212,11 +216,20 @@ int hal_update_wripc(int ms_timeout)
to prevent from launching multiple HALs simultaneously. */
int hal_check_running()
{
struct minipc_ch *ch;
struct wrs_shm_head *hal_head;
hal_head = wrs_shm_get(wrs_shm_hal, "", WRS_SHM_READ);
if (!hal_head) {
pr_info("Unable to open shm for HAL! Unable to check if there "
"is another HAL instance running. Error: %s\n",
strerror(errno));
exit(-1);
}
ch = minipc_client_create(WRSW_HAL_SERVER_ADDR, 0);
if (!ch)
/* check if pid is 0 (shm not filled) or process with provided
* pid does not exist (probably crashed) */
if ((hal_head->pid == 0) || (kill(hal_head->pid, 0) != 0))
return 0;
minipc_close(ch);
wrs_shm_put(hal_head);
return 1;
}
......@@ -16,6 +16,7 @@
#include <libwr/shw_io.h>
#include <libwr/sfp_lib.h>
#include <libwr/config.h>
#include <libwr/hal_shmem.h>
#include "wrsw_hal.h"
#include <rt_ipc.h>
......@@ -26,6 +27,7 @@
static int daemon_mode = 0;
static hal_cleanup_callback_t cleanup_cb[MAX_CLEANUP_CALLBACKS];
struct hal_shmem_header *hal_shmem;
/* Adds a function to be called during the HAL shutdown. */
int hal_add_cleanup_callback(hal_cleanup_callback_t cb)
......@@ -235,7 +237,8 @@ int main(int argc, char *argv[])
}
hal_port_update_all();
shw_update_fans();
/* update fans and temperatures in shmem */
shw_update_fans(&hal_shmem->temp);
t1 = t2;
}
......
......@@ -33,7 +33,7 @@
#define RTS_POLL_INTERVAL 200 /* ms */
#define SFP_POLL_INTERVAL 1000 /* ms */
static void *hal_port_shmem;
extern struct hal_shmem_header *hal_shmem;
/* Port table: the only item which is not "hal_port_*", as it's much used */
static struct hal_port_state *ports;
......@@ -184,8 +184,7 @@ static int hal_port_init(int index)
int hal_port_init_all()
{
int index;
struct hal_shmem_header *hal_hdr;
struct wrs_shm_head *head;
struct wrs_shm_head *hal_shmem_hdr;
pr_info("Initializing switch ports...\n");
......@@ -202,24 +201,23 @@ int hal_port_init_all()
}
/* Allocate the ports in shared memory, so wr_mon etc can see them
Use lock since some (like rtud) wait for hal to be available */
hal_port_shmem = wrs_shm_get(wrs_shm_hal, "wrsw_hal",
hal_shmem_hdr = wrs_shm_get(wrs_shm_hal, "wrsw_hal",
WRS_SHM_WRITE | WRS_SHM_LOCKED);
if (!hal_port_shmem) {
if (!hal_shmem_hdr) {
fprintf(stderr, "%s: Can't join shmem: %s\n", __func__,
strerror(errno));
return -1;
}
head = hal_port_shmem;
hal_hdr = wrs_shm_alloc(hal_port_shmem, sizeof(*hal_hdr));
ports = wrs_shm_alloc(hal_port_shmem,
hal_shmem = wrs_shm_alloc(hal_shmem_hdr, sizeof(*hal_shmem));
ports = wrs_shm_alloc(hal_shmem_hdr,
sizeof(struct hal_port_state)
* HAL_MAX_PORTS);
if (!hal_hdr || !ports) {
if (!hal_shmem || !ports) {
fprintf(stderr, "%s: can't allocate in shmem\n", __func__);
return -1;
}
hal_hdr->ports = ports;
hal_shmem->ports = ports;
for (index = 0; index < HAL_MAX_PORTS; index++)
if (hal_port_init(index) < 0)
......@@ -230,14 +228,18 @@ int hal_port_init_all()
hal_port_nports);
/* We are done, mark things as valid */
hal_hdr->nports = hal_port_nports;
head->version = HAL_SHMEM_VERSION;
hal_shmem->nports = hal_port_nports;
hal_shmem_hdr->version = HAL_SHMEM_VERSION;
hal_shmem->temp.fpga_thold =
atoi(libwr_cfg_get("SNMP_TEMP_THOLD_FPGA"));
hal_shmem->temp.pll_thold = atoi(libwr_cfg_get("SNMP_TEMP_THOLD_PLL"));
hal_shmem->temp.psl_thold = atoi(libwr_cfg_get("SNMP_TEMP_THOLD_PSL"));
hal_shmem->temp.psr_thold = atoi(libwr_cfg_get("SNMP_TEMP_THOLD_PSR"));
/* Release processes waiting for HAL's to fill shm with correct data
When shm is opened successfully data in shm is still not populated!
Read data with wrs_shm_seqbegin and wrs_shm_seqend!
Especially for nports it is important */
wrs_shm_write(head, WRS_SHM_WRITE_END);
wrs_shm_write(hal_shmem_hdr, WRS_SHM_WRITE_END);
/* Create a WRIPC server for HAL public API */
return hal_init_wripc(ports);
......@@ -442,14 +444,19 @@ static void hal_port_insert_sfp(struct hal_port_state * p)
char subname[48];
int err;
if (shw_sfp_read_verify_header(p->hw_index, &shdr) < 0) {
err = shw_sfp_read_verify_header(p->hw_index, &shdr);
if (err == -2) {
pr_error("SFP module not inserted. Failed to read SFP "
"configuration header\n");
return;
} else if (err < 0) {
pr_error("Failed to read SFP configuration header\n");
return;
}
pr_info("SFP Info: Manufacturer: %.16s P/N: %.16s, S/N: %.16s\n",
shdr.vendor_name, shdr.vendor_pn, shdr.vendor_serial);
cdata = shw_sfp_get_cal_data(p->hw_index);
cdata = shw_sfp_get_cal_data(p->hw_index, &shdr);
if (cdata) {
pr_info("SFP Info: (%s) deltaTx %d "
"delta Rx %d alpha %.3f (* 1e6)\n",
......@@ -460,17 +467,23 @@ static void hal_port_insert_sfp(struct hal_port_state * p)
memcpy(&p->calib.sfp, cdata,
sizeof(struct shw_sfp_caldata));
/* Mark SFP as found in data base */
p->calib.sfp.flags |= SFP_FLAG_IN_DB;
} else {
fprintf(stderr, "Unknown SFP \"%.16s\" on port %s\n",
shdr.vendor_pn, p->name);
fprintf(stderr, "Unknown SFP vn=\"%.16s\" pn=\"%.16s\" "
"vs=\"%.16s\" on port %s\n", shdr.vendor_name,
shdr.vendor_pn, shdr.vendor_serial, p->name);
memset(&p->calib.sfp, 0, sizeof(p->calib.sfp));
}
p->state = HAL_PORT_STATE_LINK_DOWN;
shw_sfp_set_tx_disable(p->hw_index, 0);
/* Copy the strings anyways, for informative value in shmem */
strncpy(p->calib.sfp.vendor_name, (void *)shdr.vendor_name, 16);
strncpy(p->calib.sfp.part_num, (void *)shdr.vendor_pn, 16);
strncpy(p->calib.sfp.vendor_serial, (void *)shdr.vendor_serial, 16);
/* check if SFP is 1GbE */
p->calib.sfp.flags |= shdr.br_nom == SFP_SPEED_1Gb ? SFP_FLAG_1GbE : 0;
/*
* Now, we should fix the alpha value according to fiber
......@@ -495,8 +508,10 @@ static void hal_port_insert_sfp(struct hal_port_state * p)
return;
}
fprintf(stderr, "Port %s, SFP \"%.16s\", fiber %i: no alpha known\n",
p->name, p->calib.sfp.part_num, p->fiber_index);
fprintf(stderr, "Port %s, SFP vn=\"%.16s\" pn=\"%.16s\" vs=\"%.16s\", "
"fiber %i: no alpha known\n", p->name,
p->calib.sfp.vendor_name, p->calib.sfp.part_num,
p->calib.sfp.vendor_serial, p->fiber_index);
p->calib.sfp.alpha = 0;
}
......
......@@ -95,6 +95,14 @@ void init_shm(void)
sleep(1);
}
/* check hal's shm version */
if (hal_head->version != HAL_SHMEM_VERSION) {
fprintf(stderr, "rtu_drv: unknown hal's shm version %i "
"(known is %i)\n",
hal_head->version, HAL_SHMEM_VERSION);
exit(-1);
}
/* Even after HAL restart, HAL will place structures at the same
* addresses. No need to re-dereference pointer at each read. */
hal_ports = wrs_shm_follow(hal_head, h->ports);
......
......@@ -97,6 +97,11 @@ static struct rtu_vlan_table_entry *vlan_tab;
*/
static pthread_mutex_t fd_mutex;
/**
* \brief Pointer to shmem, user for write locking.
*/
static struct wrs_shm_head *rtu_port_shmem;
static struct hw_req *tail(struct hw_req *head);
static void clean_list(struct hw_req *head);
static int hw_request(int type, struct rtu_addr addr,
......@@ -120,7 +125,6 @@ int rtu_fd_init(uint16_t poly, unsigned long aging)
{
uint32_t bitmap[RTU_ENTRIES / 32];
int err;
struct wrs_shm_head *rtu_port_shmem;
struct rtu_shmem_header *rtu_hdr;
pr_info("Open rtu shmem.\n");
rtu_port_shmem = wrs_shm_get(wrs_shm_rtu, "wrsw_rtud",
......@@ -221,6 +225,8 @@ int rtu_fd_create_entry(uint8_t mac[ETH_ALEN], uint16_t vid, uint32_t port_mask,
struct rtu_addr eaddr;
pthread_mutex_lock(&fd_mutex);
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
// if VLAN is registered (otherwise just ignore request)
if (!vlan_tab[vid].drop) {
// Obtain FID from VLAN database
......@@ -255,6 +261,9 @@ int rtu_fd_create_entry(uint8_t mac[ETH_ALEN], uint16_t vid, uint32_t port_mask,
pr_error(
"Hash %03x has no buckets left.\n",
eaddr.hash);
wrs_shm_write(rtu_port_shmem,
WRS_SHM_WRITE_END);
pthread_mutex_unlock(&fd_mutex);
return -ENOMEM;
}
......@@ -280,6 +289,7 @@ int rtu_fd_create_entry(uint8_t mac[ETH_ALEN], uint16_t vid, uint32_t port_mask,
}
rtu_fd_commit();
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
pthread_mutex_unlock(&fd_mutex);
return ret;
}
......@@ -330,22 +340,6 @@ void rtu_fd_flush(void)
pthread_mutex_unlock(&fd_mutex);
}
struct rtu_filtering_entry *rtu_fd_lookup_htab_entry(int index)
{
int i, j, n = 0;
for (i = 0; i < RTU_ENTRIES / RTU_BUCKETS; i++) {
for (j = 0; j < RTU_BUCKETS; j++) {
if (rtu_htab[i][j].valid) {
if (n == index)
return &rtu_htab[i][j];
n++;
}
}
}
return NULL;
}
//---------------------------------------------
// Static Methods
//---------------------------------------------
......@@ -417,9 +411,12 @@ static inline int to_mem_addr(struct rtu_addr addr)
*/
static void clean_fd(void)
{
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
memset(rtu_htab, 0, sizeof(*rtu_htab) * HTAB_ENTRIES);
rtu_clean_htab();
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
/**
......@@ -429,6 +426,8 @@ static void clean_vd(void)
{
int i;
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
rtu_clean_vlan();
for (i = 1; i < NUM_VLANS; i++) {
vlan_tab[i].drop = 1;
......@@ -444,6 +443,7 @@ static void clean_vd(void)
vlan_tab[0].prio = 0;
rtu_write_vlan_entry(0, &vlan_tab[0]);
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
/**
......@@ -466,6 +466,7 @@ static void rtu_fd_age_update(void)
// Update 'last access time' for accessed entries
t = now();
// HTAB
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
for (i = 0; i < RTU_ENTRIES / 32; i++)
for (j = 0; j < 32; j++) {
agr_word = bitmap[i];
......@@ -492,6 +493,8 @@ static void rtu_fd_age_update(void)
rtu_htab[hash][bucket].last_access_t = t;
}
}
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
void rtu_fd_clear_entries_for_port(int dest_port)
......@@ -500,6 +503,8 @@ void rtu_fd_clear_entries_for_port(int dest_port)
int j; // bucket loop index
struct rtu_filtering_entry *ent; /* pointer to scan tables */
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
for (i = HTAB_ENTRIES; i-- > 0;) {
for (j = RTU_BUCKETS; j-- > 0;) {
ent = &rtu_htab[i][j];
......@@ -517,6 +522,7 @@ void rtu_fd_clear_entries_for_port(int dest_port)
}
// commit changes
rtu_fd_commit();
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
/**
......@@ -531,6 +537,8 @@ static void rtu_fd_age_out(void)
unsigned long t; // (secs)
t = now() - aging_time;
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
// HTAB
for (i = HTAB_ENTRIES; i-- > 0;) {
for (j = RTU_BUCKETS; j-- > 0;) {
......@@ -548,6 +556,7 @@ static void rtu_fd_age_out(void)
}
// commit changes
rtu_fd_commit();
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
/**
......@@ -558,6 +567,8 @@ static void delete_htab_entry(struct rtu_addr addr)
{
int i, n_buckets = htab_count_buckets(addr);
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
pr_info("Deleted entry for MAC %s : hash %03x:%d.\n",
mac_to_string(rtu_htab[addr.hash][addr.bucket].mac), addr.hash,
addr.bucket);
......@@ -579,6 +590,7 @@ static void delete_htab_entry(struct rtu_addr addr)
&rtu_htab[a.hash][a.bucket],
(i == n_buckets - 1) ? 1 : 0);
}
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
static void rtu_hw_commit(void)
......@@ -642,6 +654,7 @@ void rtu_fd_create_vlan_entry(int vid, uint32_t port_mask, uint8_t fid,
#define rtu_rd(reg) \
_fpga_readl(FPGA_BASE_RTU + offsetof(struct RTU_WB, reg))
int port_num = RTU_PSR_N_PORTS_R(rtu_rd(PSR));
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_BEGIN);
/****************************************************************************************/
if (port_mask == 0x0 && drop == 1)
vlan_tab[vid].port_mask = 0x0;
......@@ -654,28 +667,5 @@ void rtu_fd_create_vlan_entry(int vid, uint32_t port_mask, uint8_t fid,
vlan_tab[vid].prio = prio;
rtu_write_vlan_entry(vid, &vlan_tab[vid]);
}
/**
* \brief Creates or updates a filtering entry in the VLAN table.
* @param vid VLAN ID
* @return entry of VLAN table at given VID-address
*/
struct rtu_vlan_table_entry *rtu_vlan_entry_get(int vid)
{
// First entry reserved for untagged packets.
if (vid > NUM_VLANS)
return NULL;
if (vlan_tab[vid].drop == 0)
vlan_entry_rd(vid);
return &vlan_tab[vid];
}
void vlan_entry_rd(int vid)
{
// First entry reserved for untagged packets.
pr_info(
"vlan_entry_vd: vid %d, drop=%d, fid=%d, port_mask 0x%x\n", vid,
vlan_tab[vid].drop, vlan_tab[vid].fid, vlan_tab[vid].port_mask);
wrs_shm_write(rtu_port_shmem, WRS_SHM_WRITE_END);
}
......@@ -62,15 +62,8 @@ void rtu_fd_set_hash_poly(uint16_t poly);
void rtu_fd_flush(void);
void rtu_fd_clear_entries_for_port(int dest_port);
struct rtu_filtering_entry *rtu_fd_lookup_htab_entry(int index);
struct rtu_filtering_entry *rtu_fd_lookup_htab_entry(int index);
void rtu_fd_create_vlan_entry(int vid, uint32_t port_mask, uint8_t fid,
uint8_t prio, int has_prio, int prio_override,
int drop);
void vlan_entry_rd(int vid);
struct rtu_vlan_table_entry *rtu_vlan_entry_get(int vid);
#endif /*__WHITERABBIT_RTU_FD_H*/
......@@ -67,14 +67,17 @@ int read_ports(void){
unsigned retries = 0;
/* read data, with the sequential lock to have all data consistent */
do {
while (1) {
ii = wrs_shm_seqbegin(hal_head);
memcpy(hal_ports_local_copy, hal_ports,
hal_nports_local*sizeof(struct hal_port_state));
retries++;
if (retries > 100)
return -1;
} while (wrs_shm_seqretry(hal_head, ii));
if (!wrs_shm_seqretry(hal_head, ii))
break; /* consistent read */
usleep(1000);
}
return 0;
......
......@@ -47,79 +47,6 @@ static struct minipc_ch *rtud_ch;
if (minipc_export(rtud_ch, &stru) < 0) { \
pr_error("Could not export %s (rtu_ch=%p)\n",stru.name,rtud_ch); }
/* The exported function */
int rtudexp_get_fd_list(const struct minipc_pd *pd, uint32_t * args, void *ret)
{
int i;
rtudexp_fd_list_t *list = ret;
int start_from = args[0];
pr_info("GetFDList start=%d\n", start_from);
for (i = 0; i < 8; i++) {
struct rtu_filtering_entry *ent =
rtu_fd_lookup_htab_entry(start_from + i);
if (!ent)
break;
memcpy(list->list[i].mac, ent->mac, sizeof(ent->mac));
//printf("Ent: %s %x\n", mac_to_string(ent->mac), ent->port_mask_dst);
list->list[i].dpm = ent->port_mask_dst;
list->list[i].spm = ent->port_mask_src;
list->list[i].priority = 0;
list->list[i].dynamic = ent->dynamic;
list->list[i].hash = ent->addr.hash;
list->list[i].bucket = ent->addr.bucket;
list->list[i].age = ent->age;
list->list[i].fid = ent->fid;
}
list->num_rules = i;
list->next = (i < 8 ? 0 : start_from + i);
return 0;
}
/* The exported vlan */
int rtudexp_get_vd_list(const struct minipc_pd *pd, uint32_t * args, void *ret)
{
int i = 0;
rtudexp_vd_list_t *list = ret;
int current = args[0];
pr_info("GetVDList start=%d\n", current);
do {
struct rtu_vlan_table_entry *ent = rtu_vlan_entry_get(current);
if (!ent)
break;
if (ent->drop == 0 || ent->port_mask != 0x0) {
list->list[i].vid = current;
list->list[i].port_mask = ent->port_mask;
list->list[i].drop = ent->drop;
list->list[i].fid = ent->fid;
list->list[i].has_prio = ent->has_prio;
list->list[i].prio_override = ent->prio_override;
list->list[i].prio = ent->prio;
pr_info(
"vlan_entry_vd: vid %d, drop=%d, fid=%d, port_mask 0x%x\n",
list->list[i].vid, list->list[i].drop,
list->list[i].fid, list->list[i].port_mask);
i++;
}
current++;
if (current == NUM_VLANS)
break;
} while (i < 8);
list->num_entries = i;
list->next = (i < 8 ? 0 : current);
return 0;
}
int rtudexp_clear_entries(const struct minipc_pd *pd,
uint32_t * args, void *ret)
{
......@@ -196,8 +123,6 @@ int rtud_init_exports()
if (getenv("RTUD_MINIPC_DEBUG"))
minipc_set_logfile(rtud_ch, stderr);
MINIPC_EXP_FUNC(rtud_export_get_fd_list, rtudexp_get_fd_list);
MINIPC_EXP_FUNC(rtud_export_get_vd_list, rtudexp_get_vd_list);
MINIPC_EXP_FUNC(rtud_export_clear_entries, rtudexp_clear_entries);
MINIPC_EXP_FUNC(rtud_export_add_entry, rtudexp_add_entry);
MINIPC_EXP_FUNC(rtud_export_vlan_entry, rtudexp_vlan_entry);
......
......@@ -31,64 +31,6 @@
#include <stdint.h>
#include <minipc.h>
typedef struct {
uint8_t mac[8];
uint32_t dpm;
uint32_t spm;
uint8_t priority;
int dynamic;
uint16_t hash;
int bucket;
int age;
int fid;
} rtudexp_fd_entry_t;
typedef struct {
rtudexp_fd_entry_t list[8];
int num_rules;
int next;
} rtudexp_fd_list_t;
///// VLAN export
typedef struct {
int vid;
uint32_t port_mask;
uint8_t fid;
uint8_t prio;
int has_prio;
int prio_override;
int drop;
} rtudexp_vd_entry_t;
typedef struct {
rtudexp_vd_entry_t list[8];
int num_entries;
int next;
} rtudexp_vd_list_t;
/* Export this function: it returns a structure */
struct minipc_pd rtud_export_get_fd_list = {
.name = "get_fd_list",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT,
rtudexp_fd_list_t),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
/* Export this function: it returns a structure */
struct minipc_pd rtud_export_get_vd_list = {
.name = "get_vd_list",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT,
rtudexp_vd_list_t),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_END,
},
};
/* Export of a function to set remove entry in rtu */
struct minipc_pd rtud_export_clear_entries = {
.name = "clear_entries",
......
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