Commit dbb08dae authored by Alessandro Rubini's avatar Alessandro Rubini

arch-wrs: added shmem code from libwr

This is respinning a previous commit, based on the new shmem in libwr
after the feedback and patches by Adam. More headers are inserted,
as they are included by the ones we use.

hal_shmem.h has one 1-line modification to account for the different
include path in ppsi, as opposed to wr-switch-sw.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent e904c963
......@@ -8,6 +8,7 @@ OBJ-y += $A/wrs-startup.o \
$A/wrs-io.o \
$A/wrs-calibration.o \
$A/wrs-ipcserver.o \
$A/shmem.o \
arch-unix/unix-conf.o \
lib/cmdline.o \
lib/conf.o \
......
#ifndef __LIBWR_HAL_SHMEM_H__
#define __LIBWR_HAL_SHMEM_H__
#include <hal_exports.h>
#include <libwr/sfp_lib.h>
/* Port state machine states */
#define HAL_PORT_STATE_DISABLED 0
#define HAL_PORT_STATE_LINK_DOWN 1
#define HAL_PORT_STATE_UP 2
#define HAL_PORT_STATE_CALIBRATION 3
#define HAL_PORT_STATE_LOCKING 4
#define DEFAULT_T2_PHASE_TRANS 0
#define DEFAULT_T4_PHASE_TRANS 0
/* Port delay calibration parameters */
typedef struct hal_port_calibration {
/* PHY delay measurement parameters for PHYs which require
external calibration (i.e. with the feedback network. */
/* minimum possible delay introduced by the PHY. Expressed as time
(in picoseconds) between the beginning of the symbol on the serial input
and the rising edge of the RX clock at which the deserialized word is
available at the parallel output of the PHY. */
uint32_t phy_rx_min;
/* the same set of parameters, but for the TX path of the PHY */
uint32_t phy_tx_min;
/* Current PHY (clock-to-serial-symbol) TX and RX delays, in ps */
uint32_t delta_tx_phy;
uint32_t delta_rx_phy;
/* Current board routing delays (between the DDMTD inputs to
the PHY clock inputs/outputs), in picoseconds */
uint32_t delta_tx_board;
uint32_t delta_rx_board;
/* When non-zero: RX path is calibrated (delta_*_rx contain valid values) */
int rx_calibrated;
/* When non-zero: TX path is calibrated */
int tx_calibrated;
struct shw_sfp_caldata sfp;
} hal_port_calibration_t;
/* Internal port state structure */
struct hal_port_state {
/* non-zero: allocated */
int in_use;
/* linux i/f name */
char name[16];
/* MAC addr */
uint8_t hw_addr[6];
/* ioctl() hw index */
int hw_index;
/* file descriptor for ioctls() */
int fd;
int hw_addr_auto;
/* port timing mode (HEXP_PORT_MODE_xxxx) */
int mode;
/* port FSM state (HAL_PORT_STATE_xxxx) */
int state;
/* unused */
int index;
/* 1: PLL is locked to this port */
int locked;
/* calibration data */
hal_port_calibration_t calib;
/* current DMTD loopback phase (ps) and whether is it valid or not */
uint32_t phase_val;
int phase_val_valid;
int tx_cal_pending, rx_cal_pending;
/* locking FSM state */
int lock_state;
/* Endpoint's base address */
uint32_t ep_base;
};
/* This is the overall structure stored in shared memory */
#define HAL_SHMEM_VERSION 1
struct hal_shmem_header {
int nports;
struct hal_port_state *ports;
};
/*
* The following functions were in userspace/wrsw_hal/hal_ports.c,
* and are used to marshall data for the RPC format. Now that we
* offer shared memory, it is the caller who must convert data to
* the expected format (which remains the RPC one as I write this).
*/
struct hal_port_state *hal_port_lookup(struct hal_port_state *ports,
const char *name);
int hal_port_query_ports(struct hexp_port_list *list,
const struct hal_port_state *ports);
int hal_port_get_exported_state(struct hexp_port_state *state,
struct hal_port_state *ports,
const char *port_name);
#endif /* __LIBWR_HAL_SHMEM_H__ */
#ifndef __LIBWR_SHW_SFPLIB_H
#define __LIBWR_SHW_SFPLIB_H
#define SFP_LED_LINK (1 << 0)
#define SFP_LED_WRMODE (1 << 1)
#define SFP_LED_SYNCED (1 << 2)
#define SFP_TX_DISABLE (1 << 3)
#define shw_sfp_set_led_link(num, status) \
shw_sfp_set_generic(num, status, SFP_LED_LINK)
#define shw_sfp_set_led_wrmode(num, status) \
shw_sfp_set_generic(num, status, SFP_LED_WRMODE)
#define shw_sfp_set_led_synced(num, status) \
shw_sfp_set_generic(num, status, SFP_LED_SYNCED)
#define shw_sfp_set_tx_disable(num, status) \
shw_sfp_set_generic(num, status, SFP_TX_DISABLE)
#define SFP_FLAG_CLASS_DATA (1 << 0)
#define SFP_FLAG_DEVICE_DATA (1 << 1)
struct shw_sfp_caldata {
int flags;
char part_num[16]; /* part number of device as found in DB */
char vendor_serial[16];
/* Callibration data */
double alpha;
uint32_t delta_tx;
uint32_t delta_rx;
struct shw_sfp_caldata *next;
};
struct shw_sfp_header {
uint8_t id;
uint8_t ext_id;
uint8_t connector;
uint8_t transciever[8];
uint8_t encoding;
uint8_t br_nom;
uint8_t reserved1;
uint8_t length1; /* Link length supported for 9/125 mm fiber (km) */
uint8_t length2; /* Link length supported for 9/125 mm fiber (100m) */
uint8_t length3; /* Link length supported for 50/125 mm fiber (10m) */
uint8_t length4; /* Link length supported for 62.5/125 mm fiber (10m) */
uint8_t length5; /* Link length supported for copper (1m) */
uint8_t reserved2;
uint8_t vendor_name[16];
uint8_t reserved3;
uint8_t vendor_oui[3];
uint8_t vendor_pn[16];
uint8_t vendor_rev[4];
uint8_t reserved4[3];
uint8_t cc_base;
/* extended ID fields start here */
uint8_t options[2];
uint8_t br_max;
uint8_t br_min;
uint8_t vendor_serial[16];
uint8_t date_code[8];
uint8_t reserved[3];
uint8_t cc_ext;
} __attribute__ ((packed));
/* Public API */
/*
* Scan all ports for plugged in SFP's. The return value is a bitmask
* of all the ports with detected SFP's (bits 0-17 are valid).
*/
uint32_t shw_sfp_module_scan(void);
/* Set/get the 4 GPIO's connected to PCA9554's for a particular SFP */
void shw_sfp_gpio_set(int num, uint8_t state);
uint8_t shw_sfp_gpio_get(int num);
static inline void shw_sfp_set_generic(int num, int status, int type)
{
uint8_t state;
state = shw_sfp_gpio_get(num);
if (status)
state |= type;
else
state &= ~type;
shw_sfp_gpio_set(num, state);
}
/* Load the db from a file */
int shw_sfp_read_db(char *filename);
/* 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);
/* return NULL if no data found */
struct shw_sfp_caldata *shw_sfp_get_cal_data(int num);
/* 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);
#endif /* __LIBWR_SHW_SFPLIB_H */
/*
* This is the shared memory interface for multi-process cooperation
* within the whiterabbit switch. Everyone exports status information.
*/
#ifndef __WRS_SHM_H__
#define __WRS_SHM_H__
#include <stdint.h>
#define WRS_SHM_FILE "/dev/shm/wrs-shmem"
#define WRS_SHM_SIZE (32*1024) /* each */
/* Every process gets 8 pages (32k) to be safe for the future */
enum wrs_shm_name {
wrs_shm_ptp,
wrs_shm_rtu,
wrs_shm_hal,
wrs_shm_vlan,
WRS_SHM_N_NAMES, /* must be last */
};
/* Each area starts with this process identifier */
struct wrs_shm_head {
void *mapbase; /* In writer's addr space (to track ptrs) */
char name[7 * sizeof(void *)];
unsigned long stamp; /* Last modified, w/ CLOCK_MONOTONIC */
unsigned long data_off; /* Where the structure lives */
int shm_name; /* The enum above, for cross-checking */
int pid; /* The current pid owning the area */
unsigned pidsequence; /* Each new pid must increments this */
unsigned sequence; /* If we need consistency, this is it */
unsigned version; /* Version of the data structure */
unsigned data_size; /* Size of it (for binary dumps) */
};
/* flags */
#define WRS_SHM_READ 0x0000
#define WRS_SHM_WRITE 0x0001
/* get vs. put, like in the kernel. Errors are in errno (see source) */
void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags);
int wrs_shm_put(void *headptr);
/* The writer can allocate structures that live in the area itself */
void *wrs_shm_alloc(void *headptr, size_t size);
/* The reader can track writer's pointers, if they are in the area */
void *wrs_shm_follow(void *headptr, void *ptr);
/* Before and after writing a chunk of data, act on sequence and stamp */
extern void wrs_shm_write(void *headptr, int begin);
/* A reader can rely on the sequence number (in the <linux/seqlock.h> way) */
extern unsigned wrs_shm_seqbegin(void *headptr);
extern int wrs_shm_seqretry(void *headptr, unsigned start);
/* A reader can check wether information is current enough */
extern int wrs_shm_age(void *headptr);
/* A reader can get the information pointer, for a specific version, or NULL */
extern void *wrs_shm_data(void *headptr, unsigned version);
#endif /* __WRS_SHM_H__ */
/* Alessandro Rubini for CERN 2014, LGPL-2.1 or later */
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <libwr/shmem.h>
/* Get wrs shared memory */
/* return NULL and set errno on error */
void *wrs_shm_get(enum wrs_shm_name name_id, char *name, unsigned long flags)
{
struct wrs_shm_head *head;
struct stat stbuf;
void *map;
int write_access = flags & WRS_SHM_WRITE;
int fd;
if (name_id >= WRS_SHM_N_NAMES) {
errno = EINVAL;
return NULL;
}
fd = open(WRS_SHM_FILE, O_RDWR | O_CREAT | O_SYNC, 0644);
if (fd < 0)
return NULL; /* keep errno */
/* The file may be too short: enlarge it as needed */
if (fstat(fd, &stbuf) < 0)
return NULL; /* keep errno */
if (stbuf.st_size < WRS_SHM_SIZE * (name_id + 1)) {
lseek(fd, WRS_SHM_SIZE * (name_id + 1) -1, SEEK_SET);
write(fd, "", 1);
}
map = mmap(0, WRS_SHM_SIZE,
PROT_READ | (write_access ? PROT_WRITE : 0),
MAP_SHARED, fd, WRS_SHM_SIZE * name_id);
if (map == MAP_FAILED)
return NULL; /* keep errno */
if (!write_access)
return map;
/* Init the fields */
head = map;
if (head->pid && kill(head->pid, 0) == 0) {
munmap(map, WRS_SHM_SIZE);
errno = EBUSY;
return NULL;
}
head->sequence = 1; /* a sort of lock */
head->mapbase = head;
strncpy(head->name, name, sizeof(head->name));
head->name[sizeof(head->name) - 1] = '\0';
head->stamp = 0;
head->data_off = sizeof(*head);
head->data_size = 0;
head->shm_name = name_id;
head->pid = getpid();
head->pidsequence++;
/* version and size are up to the user (or to allocation) */
head->sequence = 0; /* a sort of unlock */
return map;
}
/* Put wrs shared memory */
/* return 0 on success, !0 on error */
int wrs_shm_put(void *headptr)
{
struct wrs_shm_head *head = headptr;
int err;
if (head->pid == getpid())
head->pid = 0; /* mark that we are not writers any more */
if ((err = munmap(headptr, WRS_SHM_SIZE)) < 0)
return err;
return 0;
}
/* The writer can allocate structures that live in the area itself */
void *wrs_shm_alloc(void *headptr, size_t size)
{
struct wrs_shm_head *head = headptr;
void *nextptr;
if (head->pid != getpid())
return NULL; /* we are not writers */
if (head->data_off + head->data_size + size > WRS_SHM_SIZE)
return NULL; /* no space left */
nextptr = headptr + head->data_off + head->data_size;
head->data_size += (size + 7) & ~7; /* force 8-alignment */
return nextptr;
}
/* The reader can track writer's pointers, if they are in the area */
void *wrs_shm_follow(void *headptr, void *ptr)
{
struct wrs_shm_head *head = headptr;
if (ptr < head->mapbase || ptr > head->mapbase + WRS_SHM_SIZE)
return NULL; /* not in the area */
return headptr + (ptr - head->mapbase);
}
/* Before and after writing a chunk of data, act on sequence and stamp */
void wrs_shm_write(void *headptr, int begin)
{
struct wrs_shm_head *head = headptr;
struct timespec tv;
if (!begin) {
/* At end-of-writing update the timestamp too */
clock_gettime(CLOCK_MONOTONIC, &tv);
head->stamp = tv.tv_sec;
}
head->sequence++;
return;
}
/* A reader can rely on the sequence number (in the <linux/seqlock.h> way) */
unsigned wrs_shm_seqbegin(void *headptr)
{
struct wrs_shm_head *head = headptr;
return head->sequence;
}
int wrs_shm_seqretry(void *headptr, unsigned start)
{
struct wrs_shm_head *head = headptr;
if (start & 1)
return 1; /* it was odd: retry */
return head->sequence != start;
}
/* A reader can check wether information is current enough */
int wrs_shm_age(void *headptr)
{
struct wrs_shm_head *head = headptr;
struct timespec tv;
clock_gettime(CLOCK_MONOTONIC, &tv);
return tv.tv_sec - head->stamp;
}
/* A reader can get the information pointer, for a specific version, or NULL */
void *wrs_shm_data(void *headptr, unsigned version)
{
struct wrs_shm_head *head = headptr;
if (head->version != version)
return NULL;
return headptr + head->data_off;
}
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