Commit f051e6c3 authored by José Luis  Gutiérrez's avatar José Luis Gutiérrez

Merge commit 'addb9986' as 'ppsi'

parents 67c20e9a addb9986
*.map*
*.o
*.a
ppsi.bin
*.patch
*~
/ppsi
*.cmd
*.o
.tmp_*
.config
.config.old
include/config
include/generated
The authors (or people who mostly developed the code) are specified inside each
file which compose the PPSi source.
Where no otherways specified, the authors are:
Alessandro Rubini for CERN, 2011 -- GNU LGPL v2.1 or later
Aurelio Colosimo for CERN, 2011 -- GNU LGPL v2.1 or later
The whiterabbit extension is based on wrspec-pc/ptpnoposix project, internally
developed by CERN.
Many source files (mostly those regarding the algorithm itself) are based on the
PTPd project, v.2.1.0 (see http://ptpd.sourceforge.net/), with was used
according to the following copyright notice:
--- COPYRIGHT of PTPd v. 2.1.0 ---
The following copyright notice applies to all files which compose the
PTPd. This notice applies as if the text was explicitly included each
file.
Copyright (c) 2009-2010 George V. Neville-Neil, Steven Kreuzer,
Gael Mace, Alexandre Van Kempen
Copyright (c) 2005-2008 Kendall Correll, Aidan Williams
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---
This diff is collapsed.
This diff is collapsed.
mainmenu "PPSi configuration"
choice
prompt "Architecture to build for"
config ARCH_UNIX
bool "Unix (Linux and likely other systems)"
help
ARCH=unix supports standard Unix system calls, although
the code most likely includes some Linux dependencies.
Choose this architecture if you build for a Linux PC
or embedded system
config ARCH_BARE_I386
bool "Bare i386"
help
This architecture builds a Linux-i386 executable that
does not rely on the standard C library, but makes
system calls directly. This architecture is a test case
to ensure the code can run in freestanding environments
(microcontrollers or other contexts with no operating system)
config ARCH_BARE_X86_64
bool "Bare x86-64"
help
This architecture builds a Linux-x86-64 executable that
does not rely on the standard C library, but makes
system calls directly. This architecture is a test case
to ensure the code can run in freestanding environments
(microcontrollers or other contexts with no operating system)
config ARCH_WRPC
bool "White Rabbit PTP Core (WR Node)"
select CONFIG_EXT_WR
help
Build PPSi for use in the WRPC environment (SPEC card or similar
one). This is a freestanding build, without operating system.
The configuration selects the "White Rabbit" protocol extension.
config ARCH_WRS
bool "White Rabbit Switch"
select CONFIG_EXT_WR
help
Build PPSi for use in the WR switch. The computer is a standard
ARM-Linux host with hardware timestamping and internal PLLs
needed to achieve sub-ns synchronization.
The configuration selects the "White Rabbit" protocol extension.
config ARCH_SIMULATOR
bool "PPSi Simulator (hosted on Linux)"
help
Build a PPSi simulator. It's almost a unix full running slave,
with a simulated master stimulating it for test purposes. This
avoids to wait a long time to see how PPSi behaves.
This architecture uses standard Unix system calls, but the
code includes some Linux dependencies.
endchoice
config ARCH
string
default "unix" if ARCH_UNIX
default "bare-i386" if ARCH_BARE_I386
default "bare-x86-64" if ARCH_BARE_X86_64
default "wrpc" if ARCH_WRPC
default "wrs" if ARCH_WRS
default "sim" if ARCH_SIMULATOR
choice EXTENSION
prompt "Protocol Extension"
config EXT_WR
bool "White Rabbit" if ARCH_WRS || ARCH_WRPC
help
Build WR-PTP (which is able to works as a standard PTP agent,
when the peer is not WR-aware).
config EXT_NONE
bool "None"
default y
help
Select not protocol extension (build standard PTP-V2 daemon).
endchoice
config EXTENSION
string
default "whiterabbit" if EXT_WR
default "" if EXT_NONE
config CROSS_COMPILE
string "Cross compiler prefix"
default "/opt/gcc-lm32/bin/lm32-elf-" if ARCH_WRPC
default "/opt/arm-wrswitch/bin/arm-linux-" if ARCH_WRS
default ""
config ARCH_CFLAGS
string
default "-m32" if ARCH_BARE_I386
default "-m64" if ARCH_BARE_X86_64
default ""
config ARCH_LDFLAGS
string
default "-m elf_i386" if ARCH_BARE_I386
default "-m elf_x86_64" if ARCH_BARE_X86_64
default ""
config WRPCSW_ROOT
string "Source location of wrpc-sw"
depends on ARCH_WRPC
default "../wrpc-sw"
#!/bin/sh
# A trivial script to build with all known configurations
# (please add a configs/ file to test your special case)
# Prevent random user setup to invalidate building for all.
unset CROSS_COMPILE
unset ARCH
# Backup previous .config file, if present
rm -f .config.backup 2> /dev/null
cp -a .config .config.backup 2> /dev/null
# make some temporary files for logs
C=$(mktemp /tmp/ppsi-config.XXXXXX)
remove_tmp_c=true
B=$(mktemp /tmp/ppsi-build.XXXXXX)
remove_tmp_b=true
# loop on default .config files, building for all architectures
configs=$(ls configs)
for c in $configs; do
echo "##### Building with '$c'"
echo "##### Building with '$c'" >> $B
echo "##### Configuring for '$c'" >> $C
# make config and log
if ! make -s $c 2>&1 >> $C; then
echo "Error in configuration (see $C)"
remove_tmp_c=false
fi
make -s clean
# build binaries and log
if ! make -j5 2>&1 >> $B; then
echo "Build error (see $B)"
remove_tmp_b=false
fi
# print sizes
test -f ppsi.o && size ppsi.o
test -f ppsi && size ppsi | tail -n 1
done
make -s clean
# clean logs if succesful
if $remove_tmp_c; then rm $C; fi
if $remove_tmp_b; then rm $B; fi
# restore previous .config file if needed, otherwise just clean.
rm -f .config 2> /dev/null
mv -f .config.backup .config 2> /dev/null
#
# Alessandro Rubini for CERN, 2011,2013 -- public domain
#
# We are now Kconfig-based
-include $(CURDIR)/.config
# We still accept command-line choices like we used to do.
# Also, we must remove the quotes from these Kconfig values
PROTO_EXT ?= $(patsubst "%",%,$(CONFIG_EXTENSION))
ARCH ?= $(patsubst "%",%,$(CONFIG_ARCH))
CROSS_COMPILE ?= $(patsubst "%",%,$(CONFIG_CROSS_COMPILE))
WRPCSW_ROOT ?= $(patsubst "%",%,$(CONFIG_WRPCSW_ROOT))
# For "make config" to work, we need a valid ARCH
ifeq ($(ARCH),)
ARCH = unix
endif
#### In theory, users should not change stuff below this line (but please read)
# classic cross-compilation tool-set
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
# To cross-build bare stuff (x86-64 under i386 and vice versa). We need this:
LD := $(LD) $(shell echo $(CONFIG_ARCH_LDFLAGS))
CC := $(CC) $(shell echo $(CONFIG_ARCH_CFLAGS))
# (I apologize, but I couldn't find another way to remove quotes)
# Instead of repeating "ppsi" over and over, bless it TARGET
TARGET = ppsi
# we should use linux/scripts/setlocalversion instead...
VERSION = $(shell git describe --always --dirty)
# The main target is the big object file.
all: $(TARGET).o
# CFLAGS to use. Both this Makefile (later) and app-makefile may grow CFLAGS
CFLAGS = $(USER_CFLAGS)
CFLAGS += -Wall -O2 -ggdb -Iinclude -fno-common
CFLAGS += -DPPSI_VERSION=\"$(VERSION)\"
# to avoid ifdef as much as possible, I use the kernel trick for OBJ variables
OBJ-y := fsm.o diag.o
# Include arch code. Each arch chooses its own time directory..
include arch-$(ARCH)/Makefile
# include pp_printf code, by default the "full" version. Please
# set CONFIG_PRINTF_NONE or CONFIG_PRINTF_XINT if needed.
ifndef CONFIG_NO_PRINTF
OBJ-y += pp_printf/pp-printf.o
pp_printf/pp-printf.o: $(wildcard pp_printf/*.[ch])
$(MAKE) -C pp_printf pp-printf.o CC="$(CC)" LD="$(LD)"
endif
# We need this -I so <arch/arch.h> can be found
CFLAGS += -Iarch-$(ARCH)/include
# proto-standard is always included, as it provides default function
# so the extension can avoid duplication of code.
ifneq ($(PROTO_EXT),)
include proto-ext-$(PROTO_EXT)/Makefile
endif
include proto-standard/Makefile
# ...and the TIME choice sets the default operations
CFLAGS += -DDEFAULT_TIME_OPS=$(TIME)_time_ops
CFLAGS += -DDEFAULT_NET_OPS=$(TIME)_net_ops
export CFLAGS
# And this is the rule to build our target.o file. The architecture may
# build more stuff. Please note that ./MAKEALL looks for $(TARGET)
# (i.e., the ELF which is either the output or the input to objcopy -O binary)
#
# The object only depends on OBJ-y because each subdirs added needed
# libraries: see proto-standard/Makefile as an example.
$(TARGET).o: $(OBJ-y)
$(LD) -Map $(TARGET).map1 -r -o $@ $(PPSI_O_LDFLAGS) \
--start-group $(OBJ-y) --end-group
$(OBJ-y): .config $(wildcard include/ppsi/*.h)
# Finally, "make clean" is expected to work
clean:
rm -f $$(find . -name '*.[oa]' ! -path './scripts/kconfig/*') *.bin $(TARGET) *~ $(TARGET).map*
# following targets from Makefile.kconfig
silentoldconfig:
@mkdir -p include/config
$(MAKE) -f Makefile.kconfig $@
%config:
$(MAKE) -f Makefile.kconfig $@
defconfig:
@echo "Using unix_defconfig"
@$(MAKE) -f Makefile.kconfig unix_defconfig
.config: silentoldconfig
VERSION = $(shell git describe --always --dirty)
export VERSION
# Do not:
# o use make's built-in rules and variables
# (this increases performance and avoids hard-to-debug behaviour);
# o print "Entering directory ...";
MAKEFLAGS += -rR --no-print-directory
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands
ifdef V
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;
# That's our default target when none is given on the command line
PHONY := _all
_all:
# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
# Make variables (CC, etc...)
HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
export HOSTCC HOSTCXX HOSTCFLAGS HOSTCXXFLAGS
KBUILD_KCONFIG = $(CURDIR)/Kconfig
export KBUILD_KCONFIG
srctree := $(CURDIR)
objtree := $(CURDIR)
src := $(srctree)
obj := $(objtree)
export srctree objtree
# We need some generic definitions (do not try to remake the file).
$(srctree)/scripts/Kbuild.include: ;
include $(srctree)/scripts/Kbuild.include
# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
config: scripts_basic FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
clean:
@find . $(RCS_FIND_IGNORE) \
\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
-o -name 'Module.markers' -o -name '.tmp_*.o.*' \) \
-type f -print | xargs rm -f -v
PHONY += FORCE
FORCE:
# Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)
CFLAGS += -ffreestanding -Os -fno-stack-protector -Itools
ARCH_LDFLAGS = -nostdlib -static -T $A/$(ARCH).lds
# We need to undefine _FORTIFY_SOURCE, to prevent picking up external
# symbols in the inline versions of strcpy etc. This is documented in
# "man gcc" in the Ubuntu version.
CFLAGS += -U_FORTIFY_SOURCE
# All files are under A (short for ARCH) or L (short for lib): I'm lazy
A := arch-$(ARCH)
L := lib-bare
CFLAGS += -Ilib-bare
OBJ-y += $A/crt0.o \
$L/bare-startup.o \
$L/main-loop.o \
$L/bare-io.o \
$A/syscalls.o \
lib/libc-functions.o \
lib/dump-funcs.o \
lib/cmdline.o \
lib/div64.o
# We only support "bare" time operations
TIME := bare
include time-bare/Makefile
all: $(TARGET)
# to build the target, we need -lstd again, in case we call functions that
# were not selected yet (e.g., pp_init_globals() ).
$(TARGET): $(TARGET).o
$(CC) -Wl,-Map,$(TARGET).map2 $(ARCH_LDFLAGS) -o $@ $(TARGET).o
OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_ppsi_start)
SECTIONS
{
. = 0x10000000; /* A random address, non-standard by choice*/
.text : {
_ppsi_start = .;
*(.boot)
*(.text)
}
.rodata : { *(.rodata) }
.data : { *(.data) }
.bss : {
. = ALIGN(16);
__bss_start = .;
*(.bss);
. = ALIGN(16);
__bss_end = .;
}
}
/*
* A stupid crt0.S for "freestanding" stuff on gnu/linux
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
.section .boot, "ax"
.extern ppsi_main
call ppsi_clear_bss /* In C, lazy me */
/* inspired from uClib */
popl %ecx /* Store argc into %ecx */
movl %esp,%ebx /* Store argv into ebx */
movl %esp,%eax /* Store argv into eax as well*/
movl %ecx,%edx /* Stick argc into %edx so we can do some math in a sec */
leal 4(%eax,%edx,4),%eax
pushl %eax /* Environment pointer */
pushl %ebx /* Argument pointer */
pushl %ecx /* And the argument count */
call ppsi_main
.end
\ No newline at end of file
#ifndef __ARCH_H__
#define __ARCH_H__
/* Architecture-specific defines, included by top-level stuff */
/* please note that these have multiple evaluation of the argument */
#define htons(x) __bswap_16(x)
#define __bswap_16(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define htonl(x) __bswap_32(x)
#define __bswap_32(x) ( \
(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))
#define ntohs htons
#define ntohl htonl
#define abs(x) ((x >= 0) ? x : -x)
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
#undef PP_DEFAULT_PROTO
#define PP_DEFAULT_PROTO PPSI_PROTO_RAW /* We only use raw ethernet */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
#include <linux/unistd.h>
#include <ppsi/ppsi.h>
#include "bare-linux.h"
#include "syscalls.h"
int bare_errno;
struct sel_arg_struct {
unsigned long n;
void *inp, *outp, *exp;
void *tvp;
};
/*
* The following lines use defines from Torvalds (linux-2.4.0: see syscalls.h)
*/
_syscall3(int, write, int, fd, const void *, buf, int, count)
_syscall1(int, exit, int, exitcode)
_syscall1(int, time, void *, tz)
_syscall3(int, ioctl, int, fd, int, cmd, void *, arg)
_syscall1(int, select, struct sel_arg_struct *, as)
static _syscall2(int, socketcall, int, call, unsigned long *, args)
_syscall2(int, gettimeofday, void *, tv, void *, z);
_syscall2(int, settimeofday, void *, tv, void *, z);
_syscall1(int, adjtimex, void *, tv);
_syscall2(int, clock_gettime, int, clock, void *, t);
_syscall1(int, close, int, fd);
/*
* In the bare arch I'd better use sys_ prefixed names
*/
int sys_write(int fd, const void *buf, int count)
__attribute__((alias("write")));
void sys_exit(int exitval)
__attribute__((alias("exit")));
int sys_time(int tz)
__attribute__((alias("time")));
int sys_ioctl(int fd, int cmd, void *arg)
__attribute__((alias("ioctl")));
int sys_close(int fd)
__attribute__((alias("close")));
static struct sel_arg_struct as; /* declared as local, it won't work */
int sys_select(int max, void *in, void *out, void *exc, void *tout)
{
as.n = max;
as.inp = in;
as.outp = out;
as.exp = exc;
as.tvp = tout;
return select(&as);
}
int sys_gettimeofday(void *tv, void *z)
{
return gettimeofday(tv, z);
}
int sys_settimeofday(void *tv, void *z)
{
return settimeofday(tv, z);
}
int sys_adjtimex(void *tv)
{
return adjtimex(tv);
}
int sys_clock_gettime(int clock, void *t)
{
return clock_gettime(clock, t);
}
/* i386 has the socketcall thing. Bah! */
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_PACCEPT 18 /* sys_paccept(2) */
static unsigned long args[5];
int sys_socket(int domain, int type, int proto)
{
/*
* Strangely, this is not working for me:
* unsigned long args[3] = {domain, type, proto};
* So let's use an external thing. Who knows why...
*/
args[0] = domain;
args[1] = type;
args[2] = proto;
return socketcall(SYS_SOCKET, args);
}
int sys_bind(int fd, const struct bare_sockaddr *addr, int addrlen)
{
args[0] = fd;
args[1] = (unsigned long)addr;
args[2] = addrlen;
return socketcall(SYS_BIND, args);
}
int sys_recv(int fd, void *pkt, int plen, int flags)
{
args[0] = fd;
args[1] = (unsigned long)pkt;
args[2] = plen;
args[3] = flags;
return socketcall(SYS_RECV, args);
}
int sys_send(int fd, void *pkt, int plen, int flags)
{
args[0] = fd;
args[1] = (unsigned long)pkt;
args[2] = plen;
args[3] = flags;
return socketcall(SYS_SEND, args);
}
int sys_shutdown(int fd, int flags)
{
args[0] = fd;
args[1] = flags;
return socketcall(SYS_SHUTDOWN, args);
}
int sys_setsockopt(int fd, int level, int optname, const void *optval,
int optlen)
{
args[0] = fd;
args[1] = level;
args[2] = optname;
args[3] = (unsigned long)optval;
args[4] = optlen;
return socketcall(SYS_SETSOCKOPT, args);
}
/*
* From: linux-2.4.0::include/asm-i386/unistd.h
*/
extern int bare_errno;
/*
* user-visible error numbers are in the range -1 - -4096
*/
#define __syscall_return(type, res) \
do { \
if ((unsigned long)(res) >= (unsigned long)(-4096)) { \
bare_errno = -(res); \
res = -1; \
} \
return (type) (res); \
} while (0)
/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
#define _syscall0(type, name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
__syscall_return(type, __res); \
}
#define _syscall1(type, name, type1, arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name), "b" ((long)(arg1))); \
__syscall_return(type, __res); \
}
#define _syscall2(type, name, type1, arg1, type2, arg2) \
type name(type1 arg1, type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name), "b" ((long)(arg1)), "c" ((long)(arg2))); \
__syscall_return(type, __res); \
}
#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
type name(type1 arg1, type2 arg2, type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name), "b" ((long)(arg1)), "c" ((long)(arg2)), \
"d" ((long)(arg3))); \
__syscall_return(type, __res); \
}
#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \
type4, arg4) \
type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name), "b" ((long)(arg1)), "c" ((long)(arg2)), \
"d" ((long)(arg3)), "S" ((long)(arg4))); \
__syscall_return(type, __res); \
}
#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \
type4, arg4, type5, arg5) \
type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name), "b" ((long)(arg1)), "c" ((long)(arg2)), \
"d" ((long)(arg3)), "S" ((long)(arg4)), "D" ((long)(arg5))); \
__syscall_return(type, __res); \
}
#define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \
type4, arg4, type5, arg5, type6, arg6) \
type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, \
type6 arg6) \
{ \
long __res; \
__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; " \
"movl %1,%%eax ; int $0x80 ; pop %%ebp" \
: "=a" (__res) \
: "i" (__NR_##name), "b" ((long)(arg1)), "c" ((long)(arg2)), \
"d" ((long)(arg3)), "S" ((long)(arg4)), "D" ((long)(arg5)), \
"0" ((long)(arg6))); \
__syscall_return(type, __res); \
}
CFLAGS += -ffreestanding -Os -fno-stack-protector -Itools
ARCH_LDFLAGS = -nostdlib -static -T $A/$(ARCH).lds
# We need to undefine _FORTIFY_SOURCE, to prevent picking up external
# symbols in the inline versions of strcpy etc. This is documented in
# "man gcc" in the Ubuntu version.
CFLAGS += -U_FORTIFY_SOURCE
# All files are under A (short for ARCH) or L (short for lib): I'm lazy
A := arch-$(ARCH)
L := lib-bare
CFLAGS += -Ilib-bare
OBJ-y += $A/crt0.o \
$L/bare-startup.o \
$L/main-loop.o \
$L/bare-io.o \
$A/syscall.o \
$A/syscalls.o \
lib/libc-functions.o \
lib/dump-funcs.o \
lib/cmdline.o \
lib/div64.o
# We only support "bare" time operations
TIME := bare
include time-bare/Makefile
$(LIBARCH): $(OBJ-libarch)
$(AR) r $@ $^
all: $(TARGET)
# to build the target, we need -lstd again, in case we call functions that
# were not selected yet (e.g., pp_init_globals() ).
$(TARGET): $(TARGET).o
$(CC) -Wl,-Map,$(TARGET).map2 $(ARCH_LDFLAGS) -o $@ $(TARGET).o
OUTPUT_FORMAT("elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_ppsi_start)
SECTIONS
{
. = 0x10000000; /* A random address, non-standard by choice*/
.text : {
_ppsi_start = .;
*(.boot)
*(.text)
}
.rodata : { *(.rodata) }
.data : { *(.data) }
.bss : {
. = ALIGN(16);
__bss_start = .;
*(.bss);
. = ALIGN(16);
__bss_end = .;
}
}
/*
* A stupid crt0.S for "freestanding" stuff on gnu/linux
*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
.section .boot, "ax"
.extern ppsi_main
call ppsi_clear_bss /* In C, lazy me */
/* inspired from uClib */
/* Pop argc and place it in the first parameter-passing register. */
popq %rdi
/* Place argv in the second parameter-passing register. */
movq %rsp, %rsi
/* Align the stack at a 16-byte boundary. */
andq $~15, %rsp
call ppsi_main
.end
\ No newline at end of file
#ifndef __ARCH_H__
#define __ARCH_H__
/* Architecture-specific defines, included by top-level stuff */
/* please note that these have multiple evaluation of the argument */
#define htons(x) __bswap_16(x)
#define __bswap_16(x) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define htonl(x) __bswap_32(x)
#define __bswap_32(x) ( \
(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))
#define ntohs htons
#define ntohl htonl
#define abs(x) ((x >= 0) ? x : -x)
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
#undef PP_DEFAULT_PROTO
#define PP_DEFAULT_PROTO PPSI_PROTO_RAW /* We only use raw ethernet */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
/* Copied from uClibc::libc/sysdeps/linux/x86_6/syscall.S
/* Copyright (C) 2001, 2003 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
/* Please consult the file sysdeps/unix/sysv/linux/x86-64/sysdep.h for
more information about the value -4095 used below. */
/* Usage: long syscall (syscall_number, arg1, arg2, arg3, arg4, arg5, arg6)
We need to do some arg shifting, the syscall_number will be in
rax. */
.text
.globl syscall
.type syscall,%function
.align 16
syscall:
movq %rdi, %rax /* Syscall number -> rax. */
movq %rsi, %rdi /* shift arg1 - arg5. */
movq %rdx, %rsi
movq %rcx, %rdx
movq %r8, %r10
movq %r9, %r8
movq 8(%rsp),%r9 /* arg6 is on the stack. */
syscall /* Do the system call. */
cmpq $-4095, %rax /* Check %rax for error. */
jae __syscall_error /* Branch forward if it failed. */
ret /* Return to caller. */
.size syscall,.-syscall
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
int bare_errno;
/* This function from uClibc::libc/sysdeps/linux/x86_6/__syscall_error.c */
/* Wrapper for setting errno.
*
* Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
*
* Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
*/
#include <errno.h>
#include <features.h>
/* This routine is jumped to by all the syscall handlers, to stash
* an error number into errno. */
int __syscall_error(void)
{
register int err_no __asm__ ("%rcx");
__asm__ ("mov %rax, %rcx\n\t"
"neg %rcx");
bare_errno = err_no; /* changed for ptp-proposal/proto */
return -1;
}
/* end of copy from libc/sysdeps/linux/x86_6/__syscall_error.c */
#include <linux/unistd.h>
#include <ppsi/ppsi.h>
#include "bare-linux.h"
/*
* We depends on syscall.S that does the register passing
* Usage: long syscall (syscall_number, arg1, arg2, arg3, arg4, arg5, arg6)
*/
extern long syscall(uint64_t n, uint64_t arg1, uint64_t arg2, uint64_t arg3,
uint64_t arg4, uint64_t arg5, uint64_t arg6);
int write(int fd, const void *buf, int count)
{
return syscall(__NR_write, (uint64_t)fd, (uint64_t)buf, (uint64_t)count,
0, 0, 0);
}
int exit(int exitcode)
{
return syscall(__NR_exit, (uint64_t)exitcode, 0, 0,
0, 0, 0);
}
int time(long *t)
{
return syscall(__NR_time, (uint64_t)t, 0, 0,
0, 0, 0);
}
int ioctl(int fd, int cmd, void *arg)
{
return syscall(__NR_ioctl, (uint64_t)fd, (uint64_t)cmd, (uint64_t)arg,
0, 0, 0);
}
/*
* In the bare arch I'd better use sys_ prefixed names
*/
int sys_write(int fd, const void *buf, int count)
__attribute__((alias("write")));
void sys_exit(int exitval)
__attribute__((alias("exit")));
int sys_time(int tz)
__attribute__((alias("time")));
int sys_ioctl(int fd, int cmd, void *arg)
__attribute__((alias("ioctl")));
int sys_select(int max, void *in, void *out, void *exc, void *tout)
{
return syscall(__NR_select, (uint64_t)max, (uint64_t)in, (uint64_t)out,
(uint64_t) exc, (uint64_t)tout, 0);
}
int sys_socket(int domain, int type, int proto)
{
return syscall(__NR_socket, (uint64_t)domain, (uint64_t)type,
(uint64_t)proto, 0, 0, 0);
}
int sys_bind(int fd, const struct bare_sockaddr *addr, int addrlen)
{
return syscall(__NR_bind, (uint64_t)fd, (uint64_t)addr,
(uint64_t)addrlen, 0, 0, 0);
}
int sys_recv(int fd, void *pkt, int plen, int flags)
{
return syscall(__NR_recvfrom, (uint64_t)fd, (uint64_t)pkt,
(uint64_t)plen, (uint64_t)flags, 0, 0);
}
int sys_send(int fd, void *pkt, int plen, int flags)
{
return syscall(__NR_sendto, (uint64_t)fd, (uint64_t)pkt,
(uint64_t)plen, (uint64_t)flags, 0, 0);
}
int sys_setsockopt(int fd, int level, int optname, const void *optval,
int optlen)
{
return syscall(__NR_setsockopt, (uint64_t)fd, (uint64_t)level,
(uint64_t)optname, (uint64_t)optval, optlen, 0);
}
int sys_close(int fd)
{
return syscall(__NR_close, (uint64_t)fd, 0,
0, 0, 0, 0);
}
int sys_shutdown(int fd, int flags)
{
return syscall(__NR_shutdown, (uint64_t)fd, (uint64_t)flags,
0, 0, 0, 0);
}
int sys_gettimeofday(void *tv, void *z)
{
return syscall(__NR_gettimeofday, (uint64_t)tv, (uint64_t)z,
0, 0, 0, 0);
}
int sys_settimeofday(void *tv, void *z)
{
return syscall(__NR_settimeofday, (uint64_t)tv, (uint64_t)z,
0, 0, 0, 0);
}
int sys_adjtimex(void *tv)
{
return syscall(__NR_adjtimex, (uint64_t)tv, 0,
0, 0, 0, 0);
}
int sys_clock_gettime(int clock, void *t)
{
return syscall(__NR_clock_gettime, (uint64_t)clock, (uint64_t)t,
0, 0, 0, 0);
}
# All files are under A (short for ARCH): I'm lazy
A := arch-$(ARCH)
CFLAGS += -Itools
OBJ-y += $A/sim-startup.o \
$A/main-loop.o \
$A/sim-io.o \
$A/sim-conf.o \
lib/cmdline.o \
lib/conf.o \
lib/dump-funcs.o \
lib/libc-functions.o \
lib/div64.o
# Support only "sim" time operations
TIME := sim
include time-$(TIME)/Makefile
all: $(TARGET)
# to build the target, we need -lstd again, in case we call functions that
# were not selected yet (e.g., pp_open_globals() ).
$(TARGET): $(TARGET).o
$(CC) -Wl,-Map,$(TARGET).map2 -o $@ $(TARGET).o -lrt
#ifndef __ARCH_H__
#define __ARCH_H__
/* Architecture-specific defines, included by top-level stuff */
#include <arpa/inet.h> /* ntohs etc */
#include <stdlib.h> /* abs */
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
/* nothing to do here, we keep project-wide defaults */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
*
* Released to the public domain
*/
/*
* This is the main loop for the simulator.
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <linux/if_ether.h>
#include <ppsi/ppsi.h>
#include "ppsi-sim.h"
/* Call pp_state_machine for each instance. To be called periodically,
* when no packets are incoming */
static int run_all_state_machines(struct pp_globals *ppg)
{
int j;
int delay_ms = 0, delay_ms_j;
for (j = 0; j < ppg->nlinks; j++) {
struct pp_instance *ppi = INST(ppg, j);
sim_set_global_DS(ppi);
delay_ms_j = pp_state_machine(ppi, NULL, 0);
/* delay_ms is the least delay_ms among all instances */
if (j == 0)
delay_ms = delay_ms_j;
if (delay_ms_j < delay_ms)
delay_ms = delay_ms_j;
}
return delay_ms;
}
void sim_main_loop(struct pp_globals *ppg)
{
struct pp_instance *ppi;
struct sim_ppg_arch_data *data = SIM_PPG_ARCH(ppg);
int64_t delay_ns, tmp_ns;
int j, i;
/* Initialize each link's state machine */
for (j = 0; j < ppg->nlinks; j++) {
ppi = INST(ppg, j);
ppi->is_new_state = 1;
}
delay_ns = run_all_state_machines(ppg) * 1000LL * 1000LL;
while (data->sim_iter_n <= data->sim_iter_max) {
/*
* If Ebest was changed in previous loop, run best
* master clock before checking for new packets, which
* would affect port state again
*/
if (ppg->ebest_updated) {
for (j = 0; j < ppg->nlinks; j++) {
int new_state;
struct pp_instance *ppi = INST(ppg ,j);
new_state = bmc(ppi);
if (new_state != ppi->state) {
ppi->state = new_state;
ppi->is_new_state = 1;
}
}
ppg->ebest_updated = 0;
}
while (data->n_pending && data->pending->delay_ns <= delay_ns) {
ppi = INST(ppg, data->pending->which_ppi);
sim_fast_forward_ns(ppg, data->pending->delay_ns);
delay_ns -= data->pending->delay_ns;
i = ppi->n_ops->recv(ppi, ppi->rx_frame,
PP_MAX_FRAME_LENGTH - 4,
&ppi->last_rcv_time);
if (i < PP_MINIMUM_LENGTH) {
pp_diag(ppi, frames, 1, "Error or short frame: "
"%d < %d\n", i, PP_MINIMUM_LENGTH);
continue;
}
sim_set_global_DS(ppi);
tmp_ns = 1000LL * 1000LL * pp_state_machine(ppi,
ppi->rx_ptp, i - NP(ppi)->ptp_offset);
if (tmp_ns < delay_ns)
delay_ns = tmp_ns;
}
/* here we have no pending packets or the timeout for a state
* machine is expired (so delay_ns == 0). If the timeout is not
* expired we just fast forward till it's not expired, since we
* know that there are no packets pending. */
sim_fast_forward_ns(ppg, delay_ns);
delay_ns = run_all_state_machines(ppg) * 1000LL * 1000LL;
}
return;
}
/*
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
*
* Released to the public domain
*/
/*
* This structure represents a clock abstraction. You can use it to
* have different pp_instances with different notions of time.
* When you try to get the time the sim_get_time function is performing the
* calculations needed to convert the raw data from the "real" clock to the
* timescale of the simulated one.
* When you set the time you're not actually setting it in the hardware but
* only in the parameters in this structure.
*/
struct pp_sim_time_instance {
int64_t current_ns; // with nsecs it's enough for ~300 years
int64_t freq_ppb_real; // drift of the simulated hw clock
int64_t freq_ppb_servo; // drift applied from servo to correct the hw
// Future parameters can be added
};
/*
* This structure holds the parameter representing the delays on the outgoing
* link of every pp_instance. All the values are expressed in the *absolute*
* timescale, which is represented by the master time.
*/
struct pp_sim_net_delay {
unsigned int t_prop_ns; // propagation delay on outgoing link
uint64_t jit_ns; // jitter in nsec on outgoing link
uint64_t last_outgoing_jit_ns;
};
/*
* This structure represets a pending packet. which_ppi is the destination ppi,
* chtype is the channel and delay_ns is the time that should pass from when the
* packet is sent until it will be received from the destination.
* Every time a packet is sent a structure like this is filled and stored in the
* ppg->arch_data so that we always know which is the first packet that has to
* be received and when.
*/
struct sim_pending_pkt {
int64_t delay_ns;
int which_ppi;
int chtype;
};
/*
* Structure holding an array of pending packets. 64 does not have a special
* meaning. They could be less if you look inside the ptp specification, but
* I put 64 just to be sure.
* The aim of this structure is to store information on flying packets and when
* the'll be received, because the standard state machine timeouts are not
* enough for this. Infact the main loop need to know if there are some packets
* arriving and when, otherwise it will not know how much fast forwarding is
* needed. If you fast forward based on timeouts they will expire before any
* packet has arrived and the state machine will do nothing.
*/
struct sim_ppg_arch_data {
int n_pending;
struct sim_pending_pkt pending[64];
int64_t sim_iter_max;
int64_t sim_iter_n;
};
static inline struct sim_ppg_arch_data *SIM_PPG_ARCH(struct pp_globals *ppg)
{
return (struct sim_ppg_arch_data *)(ppg->arch_data);
}
/*
* Structure holding parameters and informations restricted to just a single
* instance, like timing informations. Infact in the simulator, even if both
* master and slave are ppi inside the same ppg, they act like every one of
* them had its own clock.
* Some more data we need in every instance are Data Sets, runtime options,
* servo and TimeProperties. We need them because, since we have two ppi
* acting like two different machines, we have to use different copies of
* these data for every one of them. So we just put the per-instance data
* here and we will the pointers in ppg according to our needs every time
* we will need to use them.
* Even more stuff can be added if needed
*/
struct sim_ppi_arch_data {
struct pp_sim_time_instance time;
struct pp_sim_net_delay n_delay;
/* servo */
struct pp_servo *servo;
/* Runtime options */
struct pp_runtime_opts *rt_opts;
/* Data sets */
DSDefault *defaultDS;
DSCurrent *currentDS;
DSParent *parentDS;
DSTimeProperties *timePropertiesDS;
/* other pp_instance, used in net ops */
struct pp_instance *other_ppi;
};
/* symbolic names to address master and slave in ppg->pp_instances */
#define SIM_SLAVE 1
#define SIM_MASTER 0
static inline struct sim_ppi_arch_data *SIM_PPI_ARCH(struct pp_instance *ppi)
{
return (struct sim_ppi_arch_data *)(ppi->arch_data);
}
static inline struct pp_instance *pp_sim_get_master(struct pp_globals *ppg)
{
return INST(ppg, SIM_MASTER);
}
static inline struct pp_instance *pp_sim_get_slave(struct pp_globals *ppg)
{
return INST(ppg, SIM_SLAVE);
}
static inline int pp_sim_is_master(struct pp_instance *ppi)
{
return ((ppi - ppi->glbs->pp_instances) == SIM_MASTER);
}
static inline int pp_sim_is_slave(struct pp_instance *ppi)
{
return ((ppi - ppi->glbs->pp_instances) == SIM_SLAVE);
}
extern int sim_fast_forward_ns(struct pp_globals *ppg, int64_t ff_ns);
extern int sim_set_global_DS(struct pp_instance *ppi);
extern void sim_main_loop(struct pp_globals *ppg);
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
*
* Released according to GNU LGPL, version 2.1 or any later
*/
#include <ppsi/ppsi.h>
#include "ppsi-sim.h"
static int f_ppm_real(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct pp_instance *ppi_slave;
/* master clock is supposed to be perfect. parameters about ppm are
* modifiable only for slave ppi */
ppi_slave = pp_sim_get_slave(ppg);
SIM_PPI_ARCH(ppi_slave)->time.freq_ppb_real = arg->i * 1000;
return 0;
}
static int f_ppm_servo(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct pp_instance *ppi_slave;
/* master clock is supposed to be perfect. parameters about ppm are
* modifiable only for slave ppi */
ppi_slave = pp_sim_get_slave(ppg);
SIM_PPI_ARCH(ppi_slave)->time.freq_ppb_servo = arg->i * 1000;
return 0;
}
static int f_ofm(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct pp_sim_time_instance *t_master, *t_slave;
t_master = &SIM_PPI_ARCH(pp_sim_get_master(ppg))->time;
t_slave = &SIM_PPI_ARCH(pp_sim_get_slave(ppg))->time;
t_slave->current_ns = t_master->current_ns + arg->ts.tv_nsec +
arg->ts.tv_sec * (long long)PP_NSEC_PER_SEC;
return 0;
}
static int f_init_time(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct pp_sim_time_instance *t_inst;
t_inst = &SIM_PPI_ARCH(pp_sim_get_master(ppg))->time;
t_inst->current_ns = arg->ts.tv_nsec +
arg->ts.tv_sec * (long long)PP_NSEC_PER_SEC;
return 0;
}
static int f_fwd_t_prop(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct sim_ppi_arch_data *data;
data = SIM_PPI_ARCH(pp_sim_get_master(ppg));
data->n_delay.t_prop_ns = arg->i;
return 0;
}
static int f_bckwd_t_prop(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct sim_ppi_arch_data *data;
data = SIM_PPI_ARCH(pp_sim_get_slave(ppg));
data->n_delay.t_prop_ns = arg->i;
return 0;
}
static int f_t_prop(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
f_fwd_t_prop(lineno, ppg, arg);
f_bckwd_t_prop(lineno, ppg, arg);
return 0;
}
static int f_fwd_jit(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct sim_ppi_arch_data *data;
data = SIM_PPI_ARCH(pp_sim_get_master(ppg));
data->n_delay.jit_ns = arg->i;
return 0;
}
static int f_bckwd_jit(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
struct sim_ppi_arch_data *data;
data = SIM_PPI_ARCH(pp_sim_get_slave(ppg));
data->n_delay.jit_ns = arg->i;
return 0;
}
static int f_jit(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
f_fwd_jit(lineno, ppg, arg);
f_bckwd_jit(lineno, ppg, arg);
return 0;
}
static int f_iter(int lineno, struct pp_globals *ppg,
union pp_cfg_arg *arg)
{
SIM_PPG_ARCH(ppg)->sim_iter_max = arg->i;
return 0;
}
struct pp_argline pp_arch_arglines[] = {
{f_ppm_real, "sim_ppm_real", ARG_INT},
{f_ppm_servo, "sim_init_ppm_servo", ARG_INT},
{f_ofm, "sim_init_ofm", ARG_TIME},
{f_init_time, "sim_init_master_time", ARG_TIME},
{f_t_prop, "sim_t_prop_ns", ARG_INT},
{f_fwd_t_prop, "sim_fwd_t_prop_ns", ARG_INT},
{f_bckwd_t_prop,"sim_bckwd_t_prop_ns", ARG_INT},
{f_jit, "sim_jit_ns", ARG_INT},
{f_fwd_jit, "sim_fwd_jit_ns", ARG_INT},
{f_bckwd_jit, "sim_bckwd_jit_ns", ARG_INT},
{f_iter, "sim_iter_max", ARG_TIME},
{}
};
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
#include <stdio.h>
#include <ppsi/ppsi.h>
void pp_puts(const char *s)
{
fputs(s, stdout);
}
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Pietro Fezzardi (pietrofezzardi@gmail.com)
*
* Released to the public domain
*/
#include <stdio.h>
#include <ppsi/ppsi.h>
#include "ppsi-sim.h"
static struct pp_runtime_opts sim_master_rt_opts = {
.clock_quality = {
.clockClass = PP_CLASS_WR_GM_LOCKED,
.clockAccuracy = PP_DEFAULT_CLOCK_ACCURACY,
.offsetScaledLogVariance = PP_DEFAULT_CLOCK_VARIANCE,
},
.inbound_latency = {0, PP_DEFAULT_INBOUND_LATENCY},
.outbound_latency = {0, PP_DEFAULT_OUTBOUND_LATENCY},
.max_rst = PP_DEFAULT_MAX_RESET,
.max_dly = PP_DEFAULT_MAX_DELAY,
.flags = PP_DEFAULT_FLAGS,
.ap = PP_DEFAULT_AP,
.ai = PP_DEFAULT_AI,
.s = PP_DEFAULT_DELAY_S,
.announce_intvl = PP_DEFAULT_ANNOUNCE_INTERVAL,
.sync_intvl = PP_DEFAULT_SYNC_INTERVAL,
.prio1 = PP_DEFAULT_PRIORITY1,
.prio2 = PP_DEFAULT_PRIORITY2,
.domain_number = PP_DEFAULT_DOMAIN_NUMBER,
.ttl = PP_DEFAULT_TTL,
};
/*
* In arch-sim we use two pp_instaces in the same pp_globals to represent
* two different machines. This means *completely differnt* machines, with
* their own Data Sets. Given we can't put more all the different Data Sets
* in the same ppg, we stored them in the ppi->arch_data of every istance.
* This function is used to set the inner Data Sets pointer of the ppg to
* point to the Data Sets related to the pp_instange passed as argument
*/
int sim_set_global_DS(struct pp_instance *ppi)
{
struct sim_ppi_arch_data *data = SIM_PPI_ARCH(ppi);
ppi->glbs->defaultDS = data->defaultDS;
ppi->glbs->currentDS = data->currentDS;
ppi->glbs->parentDS = data->parentDS;
ppi->glbs->timePropertiesDS = data->timePropertiesDS;
ppi->glbs->servo = data->servo;
ppi->glbs->rt_opts = data->rt_opts;
return 0;
}
static int sim_ppi_init(struct pp_instance *ppi, int which_ppi)
{
struct sim_ppi_arch_data *data;
ppi->proto = PP_DEFAULT_PROTO;
ppi->__tx_buffer = malloc(PP_MAX_FRAME_LENGTH);
ppi->__rx_buffer = malloc(PP_MAX_FRAME_LENGTH);
ppi->arch_data = calloc(1, sizeof(struct sim_ppi_arch_data));
ppi->portDS = calloc(1, sizeof(*ppi->portDS));
if ((!ppi->arch_data) || (!ppi->portDS))
return -1;
data = SIM_PPI_ARCH(ppi);
data->defaultDS = calloc(1, sizeof(*data->defaultDS));
data->currentDS = calloc(1, sizeof(*data->currentDS));
data->parentDS = calloc(1, sizeof(*data->parentDS));
data->timePropertiesDS = calloc(1,
sizeof(*data->timePropertiesDS));
data->servo = calloc(1, sizeof(*data->servo));
if ((!data->defaultDS) ||
(!data->currentDS) ||
(!data->parentDS) ||
(!data->timePropertiesDS) ||
(!data->servo))
return -1;
if (which_ppi == SIM_MASTER)
data->rt_opts = &sim_master_rt_opts;
else
data->rt_opts = &__pp_default_rt_opts;
data->other_ppi = INST(ppi->glbs, -(which_ppi - 1));
return 0;
}
int main(int argc, char **argv)
{
struct pp_globals *ppg;
struct pp_instance *ppi;
int i;
setbuf(stdout, NULL);
pp_printf("PPSi. Commit %s, built on " __DATE__ "\n", PPSI_VERSION);
ppg = calloc(1, sizeof(struct pp_globals));
ppg->max_links = 2; // master and slave, nothing else
ppg->arch_data = calloc(1, sizeof(struct sim_ppg_arch_data));
ppg->pp_instances = calloc(ppg->max_links, sizeof(struct pp_instance));
if ((!ppg->arch_data) || (!ppg->pp_instances))
return -1;
/* Alloc data stuctures inside the pp_instances */
for (i = 0; i < ppg->max_links; i++) {
ppi = INST(ppg, i);
ppi->glbs = ppg; // must be done before using sim_set_global_DS
if (sim_ppi_init(ppi, i))
return -1;
}
/*
* Configure the master with standard configuration, only from default
* string. The master is not configurable, but there's no need to do
* it cause we are ok with a standard one. We just want to see the
* behaviour of the slave.
* NOTE: the master instance is initialized before parsing the command
* line, so the diagnostics cannot be enabled here. We cannot put the
* master config later because the initial time for the master is needed
* to set the initial offset for the slave
*/
sim_set_global_DS(pp_sim_get_master(ppg));
pp_config_string(ppg, strdup("port SIM_MASTER; iface MASTER;"
"proto udp; role master;"
"sim_iter_max 10000;"
"sim_init_master_time .9;"));
/* parse commandline for configuration options */
sim_set_global_DS(pp_sim_get_slave(ppg));
if (pp_parse_cmdline(ppg, argc, argv) != 0)
return -1;
/* If no item has been parsed, provide default file or string */
if (ppg->cfg.cfg_items == 0)
pp_config_file(ppg, 0, PP_DEFAULT_CONFIGFILE);
if (ppg->cfg.cfg_items == 0)
pp_config_string(ppg, strdup("port SIM_SLAVE; iface SLAVE;"
"proto udp; role slave;"));
for (i = 0; i < ppg->nlinks; i++) {
ppi = INST(ppg, i);
sim_set_global_DS(ppi);
ppi->iface_name = ppi->cfg.iface_name;
ppi->port_name = ppi->cfg.port_name;
if (ppi->proto == PPSI_PROTO_RAW)
pp_printf("Warning: simulator doesn't support raw "
"ethernet. Using UDP\n");
NP(ppi)->ch[PP_NP_GEN].fd = -1;
NP(ppi)->ch[PP_NP_EVT].fd = -1;
ppi->t_ops = &DEFAULT_TIME_OPS;
ppi->n_ops = &DEFAULT_NET_OPS;
if (pp_sim_is_master(ppi))
pp_init_globals(ppg, &sim_master_rt_opts);
else
pp_init_globals(ppg, &__pp_default_rt_opts);
}
sim_main_loop(ppg);
return 0;
}
# All files are under A (short for ARCH): I'm lazy
A := arch-$(ARCH)
CFLAGS += -Itools
OBJ-y += $A/unix-startup.o \
$A/main-loop.o \
$A/unix-io.o \
$A/unix-conf.o \
lib/cmdline.o \
lib/conf.o \
lib/libc-functions.o \
lib/dump-funcs.o \
lib/drop.o \
lib/div64.o
# The user can set TIME=, but we pick unix time by default
TIME ?= unix
include time-$(TIME)/Makefile
# Unix time operations are always included as a fallback
ifneq ($(TIME),unix)
include time-unix/Makefile
endif
CFLAGS += -Itime-unix
all: $(TARGET)
# to build the target, we need -lstd again, in case we call functions that
# were not selected yet (e.g., pp_init_globals() ).
$(TARGET): $(TARGET).o
$(CC) -Wl,-Map,$(TARGET).map2 -o $@ $(TARGET).o -lrt
#ifndef __ARCH_H__
#define __ARCH_H__
/* Architecture-specific defines, included by top-level stuff */
#include <arpa/inet.h> /* ntohs etc */
#include <stdlib.h> /* abs */
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
/* nothing to do here, we keep project-wide defaults */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
/*
* This is the main loop for unix stuff.
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <linux/if_ether.h>
#include <ppsi/ppsi.h>
#include "ppsi-unix.h"
/* Call pp_state_machine for each instance. To be called periodically,
* when no packets are incoming */
static int run_all_state_machines(struct pp_globals *ppg)
{
int j;
int delay_ms = 0, delay_ms_j;
for (j = 0; j < ppg->nlinks; j++) {
struct pp_instance *ppi = INST(ppg, j);
delay_ms_j = pp_state_machine(ppi, NULL, 0);
/* delay_ms is the least delay_ms among all instances */
if (j == 0)
delay_ms = delay_ms_j;
if (delay_ms_j < delay_ms)
delay_ms = delay_ms_j;
}
return delay_ms;
}
void unix_main_loop(struct pp_globals *ppg)
{
struct pp_instance *ppi;
int delay_ms;
int j;
/* Initialize each link's state machine */
for (j = 0; j < ppg->nlinks; j++) {
ppi = INST(ppg, j);
/*
* If we are sending or receiving raw ethernet frames,
* the ptp payload is one-eth-header bytes into the frame
*/
if (ppi->proto == PPSI_PROTO_RAW)
NP(ppi)->ptp_offset = ETH_HLEN;
/*
* The main loop here is based on select. While we are not
* doing anything else but the protocol, this allows extra stuff
* to fit.
*/
ppi->is_new_state = 1;
}
delay_ms = run_all_state_machines(ppg);
while (1) {
int i;
/*
* If Ebest was changed in previous loop, run best
* master clock before checking for new packets, which
* would affect port state again
*/
if (ppg->ebest_updated) {
for (j = 0; j < ppg->nlinks; j++) {
int new_state;
struct pp_instance *ppi = INST(ppg, j);
new_state = bmc(ppi);
if (new_state != ppi->state) {
ppi->state = new_state;
ppi->is_new_state = 1;
}
}
ppg->ebest_updated = 0;
}
i = unix_net_ops.check_packet(ppg, delay_ms);
if (i < 0)
continue;
if (i == 0) {
delay_ms = run_all_state_machines(ppg);
continue;
}
/* If delay_ms is -1, the above ops.check_packet will continue
* consuming the previous timeout (see its implementation).
* This ensures that every state machine is called at least once
* every delay_ms */
delay_ms = -1;
for (j = 0; j < ppg->nlinks; j++) {
int tmp_d;
ppi = INST(ppg, j);
if ((NP(ppi)->ch[PP_NP_GEN].pkt_present) ||
(NP(ppi)->ch[PP_NP_EVT].pkt_present)) {
i = ppi->n_ops->recv(ppi, ppi->rx_frame,
PP_MAX_FRAME_LENGTH - 4,
&ppi->last_rcv_time);
if (i == -2) {
continue; /* dropped */
}
if (i == -1) {
pp_diag(ppi, frames, 1,
"Receive Error %i: %s\n",
errno, strerror(errno));
continue;
}
if (i < PP_MINIMUM_LENGTH) {
pp_diag(ppi, frames, 1,
"Short frame: %d < %d\n", i,
PP_MINIMUM_LENGTH);
continue;
}
tmp_d = pp_state_machine(ppi, ppi->rx_ptp,
i - NP(ppi)->ptp_offset);
if ((delay_ms == -1) || (tmp_d < delay_ms))
delay_ms = tmp_d;
}
}
}
}
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
/*
* These are the functions provided by the various unix files
*/
#define POSIX_ARCH(ppg) ((struct unix_arch_data *)(ppg->arch_data))
struct unix_arch_data {
struct timeval tv;
};
extern void unix_main_loop(struct pp_globals *ppg);
/*
* Copyright (C) 2014 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released according to GNU LGPL, version 2.1 or any later
*/
#include <ppsi/ppsi.h>
static int f_rxdrop(int lineno, struct pp_globals *ppg, union pp_cfg_arg *arg)
{
ppg->rxdrop = arg->i;
return 0;
}
static int f_txdrop(int lineno, struct pp_globals *ppg, union pp_cfg_arg *arg)
{
ppg->txdrop = arg->i;
return 0;
}
struct pp_argline pp_arch_arglines[] = {
{ f_rxdrop, "rx-drop", ARG_INT},
{ f_txdrop, "tx-drop", ARG_INT},
{}
};
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
#include <stdio.h>
#include <ppsi/ppsi.h>
void pp_puts(const char *s)
{
fputs(s, stdout);
}
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
/*
* This is the startup thing for hosted environments. It
* defines main and then calls the main loop.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#include <sys/timex.h>
#include <ppsi/ppsi.h>
#include "ppsi-unix.h"
/* ppg and fields */
static struct pp_globals ppg_static;
static DSDefault defaultDS;
static DSCurrent currentDS;
static DSParent parentDS;
static DSTimeProperties timePropertiesDS;
static struct pp_servo servo;
int main(int argc, char **argv)
{
struct pp_globals *ppg;
struct pp_instance *ppi;
unsigned long seed;
struct timex t;
int i;
setbuf(stdout, NULL);
pp_printf("PPSi. Commit %s, built on " __DATE__ "\n", PPSI_VERSION);
ppg = &ppg_static;
ppg->defaultDS = &defaultDS;
ppg->currentDS = &currentDS;
ppg->parentDS = &parentDS;
ppg->timePropertiesDS = &timePropertiesDS;
ppg->servo = &servo;
ppg->rt_opts = &__pp_default_rt_opts;
/* We are hosted, so we can allocate */
ppg->max_links = PP_MAX_LINKS;
ppg->arch_data = calloc(1, sizeof(struct unix_arch_data));
ppg->pp_instances = calloc(ppg->max_links, sizeof(struct pp_instance));
if ((!ppg->arch_data) || (!ppg->pp_instances)) {
fprintf(stderr, "ppsi: out of memory\n");
exit(1);
}
/* Before the configuration is parsed, set defaults */
for (i = 0; i < ppg->max_links; i++) {
ppi = INST(ppg, i);
ppi->proto = PP_DEFAULT_PROTO;
ppi->role = PP_DEFAULT_ROLE;
}
/* Set offset here, so config parsing can override it */
if (adjtimex(&t) >= 0)
timePropertiesDS.currentUtcOffset = t.tai;
if (pp_parse_cmdline(ppg, argc, argv) != 0)
return -1;
/* If no item has been parsed, provide a default file or string */
if (ppg->cfg.cfg_items == 0)
pp_config_file(ppg, 0, PP_DEFAULT_CONFIGFILE);
if (ppg->cfg.cfg_items == 0)
pp_config_string(ppg, strdup("link 0; iface eth0; proto udp"));
for (i = 0; i < ppg->nlinks; i++) {
ppi = INST(ppg, i);
NP(ppi)->ch[PP_NP_EVT].fd = -1;
NP(ppi)->ch[PP_NP_GEN].fd = -1;
ppi->glbs = ppg;
ppi->iface_name = ppi->cfg.iface_name;
ppi->port_name = ppi->cfg.port_name;
/* The following default names depend on TIME= at build time */
ppi->n_ops = &DEFAULT_NET_OPS;
ppi->t_ops = &DEFAULT_TIME_OPS;
ppi->portDS = calloc(1, sizeof(*ppi->portDS));
ppi->__tx_buffer = malloc(PP_MAX_FRAME_LENGTH);
ppi->__rx_buffer = malloc(PP_MAX_FRAME_LENGTH);
if (!ppi->portDS || !ppi->__tx_buffer || !ppi->__rx_buffer) {
fprintf(stderr, "ppsi: out of memory\n");
exit(1);
}
}
pp_init_globals(ppg, &__pp_default_rt_opts);
seed = time(NULL);
if (getenv("PPSI_DROP_SEED"))
seed = atoi(getenv("PPSI_DROP_SEED"));
ppsi_drop_init(ppg, seed);
unix_main_loop(ppg);
return 0; /* never reached */
}
CFLAGS += -ffreestanding -Os \
-ffunction-sections -fdata-sections \
-mmultiply-enabled -mbarrel-shift-enabled \
-Itools
# Root of wrpc-sw project
WRPCSW_ROOT ?= $(CONFIG_WRPCSW_ROOT)
CFLAGS += -I$(WRPCSW_ROOT)/include -I$(WRPCSW_ROOT)/include/std -I$(WRPCSW_ROOT)/softpll
PPSI_O_LDFLAGS = -u wrc_ptp_init
# Let's use the pp_printf we already have in wrpc-sw
CONFIG_NO_PRINTF = y
# All files are under A (short for ARCH): I'm lazy
A := arch-$(ARCH)
OBJ-y += \
$A/wrpc-io.o \
$A/wrpc-spll.o \
$A/wrpc-calibration.o \
$A/wrc_ptp_ppsi.o \
lib/dump-funcs.o \
lib/div64.o
# We only support "wrpc" time operations
TIME := wrpc
include time-wrpc/Makefile
$(TARGET).o: $(LIBARCH)
$(OBJ-y): wrpcsw_root_check
wrpcsw_root_check:
if ! [ -e "$(WRPCSW_ROOT)/include/wrc.h" ]; then\
echo "Error: WRPCSW_ROOT: can't find $(WRPCSW_ROOT)/include/wrc.h" >&2;\
exit 1;\
fi
#ifndef __ARCH_H__
#define __ARCH_H__
/* This arch exports wr functions, so include this for consistency checking */
#include "../proto-ext-whiterabbit/wr-api.h"
/* Architecture-specific defines, included by top-level stuff */
#define htons(x) (x)
#define htonl(x) (x)
#define ntohs htons
#define ntohl htonl
#define abs(x) ((x >= 0) ? x : -x)
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
#undef PP_DEFAULT_PROTO
#define PP_DEFAULT_PROTO PPSI_PROTO_RAW /* We only use raw ethernet */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Aurelio Colosimo <aurelio@aureliocolosimo.it>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#include <stdint.h>
#include <errno.h>
#include <ppsi/ppsi.h>
#include "wrpc.h"
#include "../proto-ext-whiterabbit/wr-api.h"
#include "../proto-ext-whiterabbit/wr-constants.h"
/* All of these live in wrpc-sw/include */
#include "minic.h"
#include "syscon.h"
#include "endpoint.h"
#include "softpll_ng.h"
#include "pps_gen.h"
#include "uart.h"
#include "rxts_calibrator.h"
extern int32_t cal_phase_transition;
int ptp_mode = WRC_MODE_UNKNOWN;
static int ptp_enabled = 0;
static struct wr_operations wrpc_wr_operations = {
.locking_enable = wrpc_spll_locking_enable,
.locking_poll = wrpc_spll_locking_poll,
.locking_disable = wrpc_spll_locking_disable,
.enable_ptracker = wrpc_spll_enable_ptracker,
.adjust_in_progress = wrpc_adjust_in_progress,
.adjust_counters = wrpc_adjust_counters,
.adjust_phase = wrpc_adjust_phase,
.read_calib_data = wrpc_read_calibration_data,
.calib_disable = wrpc_calibrating_disable,
.calib_enable = wrpc_calibrating_enable,
.calib_poll = wrpc_calibrating_poll,
.calib_pattern_enable = wrpc_calibration_pattern_enable,
.calib_pattern_disable = wrpc_calibration_pattern_disable,
.enable_timing_output = wrpc_enable_timing_output,
};
/*ppi fields*/
static DSDefault defaultDS;
static DSCurrent currentDS;
static DSParent parentDS;
static DSTimeProperties timePropertiesDS;
static struct pp_servo servo;
static struct wr_servo_state_t servo_state;
static struct wr_dsport wr_dsport = {
.ops = &wrpc_wr_operations,
};
static DSPort portDS = {
.ext_dsport = &wr_dsport
};
static int delay_ms = PP_DEFAULT_NEXT_DELAY_MS;
static int start_tics = 0;
static int last_link_up = 0;
static struct pp_globals ppg_static; /* forward declaration */
static unsigned char __tx_buffer[PP_MAX_FRAME_LENGTH];
static unsigned char __rx_buffer[PP_MAX_FRAME_LENGTH];
/* despite the name, ppi_static is not static: tests/measure_t24p.c uses it */
struct pp_instance ppi_static = {
.glbs = &ppg_static,
.portDS = &portDS,
.n_ops = &wrpc_net_ops,
.t_ops = &wrpc_time_ops,
.proto = PP_DEFAULT_PROTO,
.iface_name = "wr1",
.port_name = "wr1",
.__tx_buffer = __tx_buffer,
.__rx_buffer = __rx_buffer,
};
/* We now have a structure with all globals, and multiple ppi inside */
static struct pp_globals ppg_static = {
.pp_instances = &ppi_static,
.servo = &servo,
.defaultDS = &defaultDS,
.currentDS = &currentDS,
.parentDS = &parentDS,
.timePropertiesDS = &timePropertiesDS,
.global_ext_data = &servo_state,
};
int wrc_ptp_init()
{
sdb_find_devices();
uart_init_hw();
uart_init_sw();
pp_printf("PPSi for WRPC. Commit %s, built on " __DATE__ "\n",
PPSI_VERSION);
return 0;
}
#define LOCK_TIMEOUT_FM (4 * TICS_PER_SECOND)
#define LOCK_TIMEOUT_GM (60 * TICS_PER_SECOND)
int wrc_ptp_set_mode(int mode)
{
uint32_t start_tics, lock_timeout = 0;
struct pp_instance *ppi = &ppi_static;
struct pp_globals *ppg = ppi->glbs;
struct wr_dsport *wrp = WR_DSPOR(ppi);
typeof(ppg->rt_opts->clock_quality.clockClass) *class_ptr;
int error = 0;
/*
* We need to change the class in the default options.
* Unfortunately, ppg->rt_opts may be yet unassigned when this runs
*/
class_ptr = &__pp_default_rt_opts.clock_quality.clockClass;
ptp_mode = 0;
wrc_ptp_stop();
switch (mode) {
case WRC_MODE_GM:
wrp->wrConfig = WR_M_ONLY;
ppi->role = PPSI_ROLE_MASTER;
*class_ptr = PP_CLASS_WR_GM_LOCKED;
spll_init(SPLL_MODE_GRAND_MASTER, 0, 1);
lock_timeout = LOCK_TIMEOUT_GM;
break;
case WRC_MODE_MASTER:
wrp->wrConfig = WR_M_ONLY;
ppi->role = PPSI_ROLE_MASTER;
*class_ptr = PP_CLASS_DEFAULT;
spll_init(SPLL_MODE_FREE_RUNNING_MASTER, 0, 1);
lock_timeout = LOCK_TIMEOUT_FM;
break;
case WRC_MODE_SLAVE:
wrp->wrConfig = WR_S_ONLY;
ppi->role = PPSI_ROLE_SLAVE;
*class_ptr = PP_CLASS_SLAVE_ONLY;
spll_init(SPLL_MODE_SLAVE, 0, 1);
break;
}
start_tics = timer_get_tics();
pp_printf("Locking PLL");
wrp->ops->enable_timing_output(ppi, 0); /* later, wr_init chooses */
while (!spll_check_lock(0) && lock_timeout) {
spll_update();
timer_delay(TICS_PER_SECOND);
if (timer_get_tics() - start_tics > lock_timeout) {
pp_printf("\nLock timeout.");
error = -ETIMEDOUT;
break;
}
pp_printf(".");
}
pp_printf("\n");
/* If we can't lock to the atomic/gps, we say it in the class */
if (error && mode == WRC_MODE_GM)
*class_ptr = PP_CLASS_WR_GM_UNLOCKED;
ptp_mode = mode;
return error;
}
int wrc_ptp_get_mode()
{
return ptp_mode;
}
int wrc_ptp_start()
{
struct pp_instance *ppi = &ppi_static;
pp_init_globals(&ppg_static, &__pp_default_rt_opts);
/* Call the state machine. Being it in "Initializing" state, make
* ppsi initialize what is necessary */
delay_ms = pp_state_machine(ppi, NULL, 0);
start_tics = timer_get_tics();
WR_DSPOR(ppi)->linkUP = FALSE;
wr_servo_reset();
ptp_enabled = 1;
return 0;
}
int wrc_ptp_stop()
{
struct pp_instance *ppi = &ppi_static;
struct wr_dsport *wrp = WR_DSPOR(ppi);
wrp->ops->enable_timing_output(ppi, 0);
/* Moving fiber: forget about this parent (FIXME: shouldn't be here) */
wrp->parentWrConfig = wrp->parentWrModeOn = 0;
memset(ppi->frgn_master, 0, sizeof(ppi->frgn_master));
ppi->frgn_rec_num = 0; /* no known master */
ptp_enabled = 0;
wr_servo_reset();
pp_close_globals(&ppg_static);
return 0;
}
int wrc_ptp_update()
{
int i;
struct pp_instance *ppi = &ppi_static;
int now_link_up;
now_link_up = ep_link_up(NULL);
if (last_link_up != now_link_up) {
last_link_up = now_link_up;
if (ptp_enabled && (!now_link_up)) {
wrc_ptp_stop();
pp_printf("Link down: PTP stop\n");
}
if (!ptp_enabled && now_link_up) {
pp_printf("Link up: PTP start\n");
wrc_ptp_start();
}
}
if (!ptp_enabled)
return 0;
i = ppi->n_ops->recv(ppi, ppi->rx_frame, PP_MAX_FRAME_LENGTH - 4,
&ppi->last_rcv_time);
if ((!i) && (timer_get_tics() - start_tics < delay_ms))
return 0;
if (!i) {
/* Nothing received, but timeout elapsed */
start_tics = timer_get_tics();
delay_ms = pp_state_machine(ppi, NULL, 0);
return 0;
}
delay_ms = pp_state_machine(ppi, ppi->rx_ptp, i);
return 0;
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Aurelio Colosimo
*
* Released to the public domain
*/
#include <endpoint.h>
#include <ppsi/ppsi.h>
#include <softpll_ng.h>
#include <hal_exports.h>
#include "wrpc.h"
#include "../proto-ext-whiterabbit/wr-constants.h"
extern int32_t sfp_alpha;
int wrpc_read_calibration_data(struct pp_instance *ppi,
uint32_t *deltaTx, uint32_t *deltaRx, int32_t *fix_alpha,
int32_t *clock_period)
{
struct hal_port_state state;
wrpc_get_port_state(&state, ppi->iface_name);
/* check if the data is available */
if (fix_alpha)
/* take local alpha instead of HAL */
*fix_alpha = sfp_alpha;
if (clock_period)
*clock_period = state.clock_period;
/* check if tx is calibrated,
* if so read data */
if (state.calib.tx_calibrated) {
if (deltaTx)
*deltaTx = state.calib.delta_tx_phy
+ state.calib.sfp.delta_tx_ps
+ state.calib.delta_tx_board;
} else
return WR_HW_CALIB_NOT_FOUND;
/* check if rx is calibrated,
* if so read data */
if (state.calib.rx_calibrated) {
if (deltaRx)
*deltaRx = state.calib.delta_rx_phy
+ state.calib.sfp.delta_rx_ps
+ state.calib.delta_rx_board;
} else
return WR_HW_CALIB_NOT_FOUND;
return WR_HW_CALIB_OK;
}
/* Begin of exported functions */
int wrpc_calibrating_disable(struct pp_instance *ppi, int txrx)
{
return WR_HW_CALIB_OK;
}
int wrpc_calibrating_enable(struct pp_instance *ppi, int txrx)
{
return WR_HW_CALIB_OK;
}
int wrpc_calibrating_poll(struct pp_instance *ppi, int txrx, uint32_t *delta)
{
uint32_t delta_rx = 0, delta_tx = 0;
/* FIXME: why delta was 64bit whereas ep_get_deltas accepts 32bit? */
wrpc_read_calibration_data(ppi, &delta_tx, &delta_rx, NULL, NULL);
if (txrx == WR_HW_CALIB_TX)
*delta = delta_tx;
else
*delta = delta_rx;
return WR_HW_CALIB_READY;
}
int wrpc_calibration_pattern_enable(struct pp_instance *ppi,
unsigned int calibrationPeriod,
unsigned int calibrationPattern,
unsigned int calibrationPatternLen)
{
ep_cal_pattern_enable();
return WR_HW_CALIB_OK;
}
int wrpc_calibration_pattern_disable(struct pp_instance *ppi)
{
ep_cal_pattern_disable();
return WR_HW_CALIB_OK;
}
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
#include <ppsi/ppsi.h>
#include "wrpc.h"
#include "uart.h" /* wrpc-sw */
void pp_puts(const char *s)
{
uart_write_string(s);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Aurelio Colosimo
*
* Released to the public domain
*/
#include <stdint.h>
#include <ppsi/ppsi.h>
#include <pps_gen.h>
#include <softpll_ng.h>
#include "../proto-ext-whiterabbit/wr-constants.h"
#include <rxts_calibrator.h>
#include "wrpc.h"
extern uint32_t cal_phase_transition;
int wrpc_spll_locking_enable(struct pp_instance *ppi)
{
spll_init(SPLL_MODE_SLAVE, 0, 1);
spll_enable_ptracker(0, 1);
return WR_SPLL_OK;
}
int wrpc_spll_locking_poll(struct pp_instance *ppi, int grandmaster)
{
int locked;
static int t24p_calibrated = 0;
locked = spll_check_lock(0); /* both slave and gm mode */
if (grandmaster)
return locked ? WR_SPLL_READY : WR_SPLL_ERROR;
/* Else, slave: ensure calibration is done */
if(!locked) {
t24p_calibrated = 0;
}
else if(locked && !t24p_calibrated) {
/*run t24p calibration if needed*/
calib_t24p(WRC_MODE_SLAVE, &cal_phase_transition);
t24p_calibrated = 1;
}
return locked ? WR_SPLL_READY : WR_SPLL_ERROR;
}
int wrpc_spll_locking_disable(struct pp_instance *ppi)
{
/* softpll_disable(); */
return WR_SPLL_OK;
}
int wrpc_spll_enable_ptracker(struct pp_instance *ppi)
{
spll_enable_ptracker(0, 1);
return WR_SPLL_OK;
}
int wrpc_enable_timing_output(struct pp_instance *ppi, int enable)
{
if (enable == WR_DSPOR(ppi)->ppsOutputOn)
return WR_SPLL_OK;
WR_DSPOR(ppi)->ppsOutputOn = enable;
shw_pps_gen_enable_output(enable);
return WR_SPLL_OK;
}
int wrpc_adjust_in_progress(void)
{
return shw_pps_gen_busy() || spll_shifter_busy(0);
}
int wrpc_adjust_counters(int64_t adjust_sec, int32_t adjust_nsec)
{
if (adjust_sec)
shw_pps_gen_adjust(PPSG_ADJUST_SEC, adjust_sec);
if (adjust_nsec)
shw_pps_gen_adjust(PPSG_ADJUST_NSEC, adjust_nsec);
return 0;
}
int wrpc_adjust_phase(int32_t phase_ps)
{
spll_set_phase_shift(SPLL_ALL_CHANNELS, phase_ps);
return WR_SPLL_OK;
}
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Aurelio Colosimo
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#ifndef __WRPC_H
#define __WRPC_H
#include <ppsi/ppsi.h>
#include <hw/memlayout.h>
#include <libwr/hal_shmem.h>
/* This part is exactly wrpc-sw::wrc_ptp.h */
#define WRC_MODE_UNKNOWN 0
#define WRC_MODE_GM 1
#define WRC_MODE_MASTER 2
#define WRC_MODE_SLAVE 3
extern int ptp_mode;
int wrc_ptp_init(void);
int wrc_ptp_set_mode(int mode);
int wrc_ptp_get_mode(void);
int wrc_ptp_start(void);
int wrc_ptp_stop(void);
int wrc_ptp_update(void);
/* End of wrc-ptp.h */
extern struct pp_network_operations wrpc_net_ops;
extern struct pp_time_operations wrpc_time_ops;
/* other network stuff, bah.... */
struct wrpc_ethhdr {
unsigned char h_dest[6];
unsigned char h_source[6];
uint16_t h_proto;
} __attribute__((packed));
/* wrpc-spll.c (some should move to time-wrpc/) */
int wrpc_spll_locking_enable(struct pp_instance *ppi);
int wrpc_spll_locking_poll(struct pp_instance *ppi, int grandmaster);
int wrpc_spll_locking_disable(struct pp_instance *ppi);
int wrpc_spll_enable_ptracker(struct pp_instance *ppi);
int wrpc_adjust_in_progress(void);
int wrpc_adjust_counters(int64_t adjust_sec, int32_t adjust_nsec);
int wrpc_adjust_phase(int32_t phase_ps);
int wrpc_enable_timing_output(struct pp_instance *ppi, int enable);
/* wrpc-calibration.c */
int wrpc_read_calibration_data(struct pp_instance *ppi,
uint32_t *deltaTx, uint32_t *deltaRx,
int32_t *fix_alpha, int32_t *clock_period);
int wrpc_calibrating_disable(struct pp_instance *ppi, int txrx);
int wrpc_calibrating_enable(struct pp_instance *ppi, int txrx);
int wrpc_calibrating_poll(struct pp_instance *ppi, int txrx, uint32_t *delta);
int wrpc_calibration_pattern_enable(struct pp_instance *ppi,
unsigned int calibrationPeriod,
unsigned int calibrationPattern,
unsigned int calibrationPatternLen);
int wrpc_calibration_pattern_disable(struct pp_instance *ppi);
int wrpc_get_port_state(struct hal_port_state *port, const char *port_name);
#endif /* __WRPC_H */
# All files are under A (short for ARCH): I'm lazy
A := arch-$(ARCH)
CFLAGS += -Itools
OBJ-y += $A/wrs-startup.o \
$A/main-loop.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 \
lib/libc-functions.o \
lib/dump-funcs.o \
lib/drop.o \
lib/div64.o
# The user can set TIME=, but wrs is default
TIME ?= wrs
include time-$(TIME)/Makefile
# Unix time operations are always included as a fallback
include time-unix/Makefile
CFLAGS += -Iproto-ext-whiterabbit
# mini-rpc directory contains minipc library
export CROSS_COMPILE
MINIPC_DIR := $A/mini-rpc
MINIPC_LIB := $(MINIPC_DIR)/libminipc.a
CFLAGS += -I$(MINIPC_DIR)
.PHONY: $(MINIPC_LIB)
$(MINIPC_LIB):
$(MAKE) -C $(MINIPC_DIR)
OBJ-y += $(MINIPC_LIB)
all: $(TARGET)
# to build the target, we need -lstd again, in case we call functions that
# were not selected yet (e.g., pp_init_globals() ).
$(TARGET): $(TARGET).o
$(CC) -Wl,-Map,$(TARGET).map2 -o $@ $(TARGET).o -lrt
#ifndef __ARCH_H__
#define __ARCH_H__
/* This arch exports wr functions, so include this for consistency checking */
#include "../proto-ext-whiterabbit/wr-api.h"
/* Architecture-specific defines, included by top-level stuff */
#include <arpa/inet.h> /* ntohs etc */
#include <stdlib.h> /* abs */
#endif /* __ARCH_H__ */
#ifndef __PPSI_ARCH_CONSTANTS_H__
#define __PPSI_ARCH_CONSTANTS_H__
#ifndef __PPSI_CONSTANTS_H__
#Warning "Please include <ppsi/constants.h> before <arch/constants.h>"
#endif
/* nothing to do here, we keep project-wide defaults */
#endif /* __PPSI_ARCH_CONSTANTS_H__ */
#ifndef __HAL_EXPORTS_H
#define __HAL_EXPORTS_H
#include <stdint.h>
#define HAL_MAX_PORTS 32
#define WRSW_HAL_SERVER_ADDR "wrsw_hal"
// checks if the calibration unit is idle
#define HEXP_CAL_CMD_CHECK_IDLE 1
// enables/disables transmission of calibration pattern
#define HEXP_CAL_CMD_TX_PATTERN 2
// requests a measurement of TX delta
#define HEXP_CAL_CMD_TX_MEASURE 4
// requests a measurement of RX delta
#define HEXP_CAL_CMD_RX_MEASURE 5
#define HEXP_CAL_RESP_BUSY 1
#define HEXP_CAL_RESP_OK 0
#define HEXP_CAL_RESP_ERROR -1
#define HEXP_LOCK_CMD_START 1
#define HEXP_LOCK_CMD_CHECK 2
#define HEXP_LOCK_CMD_ENABLE_TRACKING 3
#define HEXP_LOCK_STATUS_LOCKED 0
#define HEXP_LOCK_STATUS_BUSY 1
#define HEXP_LOCK_STATUS_NONE 2
#define HEXP_PPSG_CMD_GET 0
#define HEXP_PPSG_CMD_ADJUST_PHASE 1
#define HEXP_PPSG_CMD_ADJUST_SEC 2
#define HEXP_PPSG_CMD_ADJUST_NSEC 3
#define HEXP_PPSG_CMD_POLL 4
#define HEXP_PPSG_CMD_SET_VALID 5
#define HEXP_ON 1
#define HEXP_OFF 0
#define HEXP_FREQ 0
#define HEXP_PHASE 1
/////////////////added by ML//////////
#define HEXP_EXTSRC_CMD_CHECK 0
#define HEXP_EXTSRC_STATUS_LOCKED 0
#define HEXP_LOCK_STATUS_BUSY 1
#define HEXP_EXTSRC_STATUS_NOSRC 2
/////////////////////////////////////
#define HAL_TIMING_MODE_GRAND_MASTER 0
#define HAL_TIMING_MODE_FREE_MASTER 1
#define HAL_TIMING_MODE_BC 2
typedef struct {
char port_name[16];
int pps_valid;
uint32_t current_phase_shift;
int32_t adjust_phase_shift;
int64_t adjust_sec;
int32_t adjust_nsec;
uint64_t current_sec;
uint32_t current_nsec;
} hexp_pps_params_t;
/* Port modes (hal_port_state.mode) */
#define HEXP_PORT_MODE_WR_M_AND_S 4
#define HEXP_PORT_MODE_WR_MASTER 1
#define HEXP_PORT_MODE_WR_SLAVE 2
#define HEXP_PORT_MODE_NON_WR 3
#define FIX_ALPHA_FRACBITS 40
/*
#define HEXP_PORT_TSC_RISING 1
#define HEXP_PORT_TSC_FALLING 2
*/
typedef struct {
int timing_mode; /* Free-running Master/GM/BC */
int locked_port;
} hexp_timing_state_t;
/* 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);
extern int halexp_get_timing_state(hexp_timing_state_t *state);
/* 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",
.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_pps_cmd(int cmd, hexp_pps_params_t *params);
struct minipc_pd __rpcdef_pps_cmd = {
.name = "pps_cmd",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
.args = {
MINIPC_ARG_ENCODE(MINIPC_ATYPE_INT, int),
MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, hexp_pps_params_t),
MINIPC_ARG_END,
},
};
struct minipc_pd __rpcdef_get_timing_state = {
.name = "get_timing_state",
.retval = MINIPC_ARG_ENCODE(MINIPC_ATYPE_STRUCT, hexp_timing_state_t),
.args = {
MINIPC_ARG_END,
},
};
#endif /* HAL_EXPORT_STRUCTURES */
#endif
#ifndef __LIBWR_HAL_SHMEM_H__
#define __LIBWR_HAL_SHMEM_H__
#include <hal_exports.h>
#include <libwr/sfp_lib.h>
#include <string.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;
/* fiber type, used to get alpha for SFP frequency */
int fiber_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;
/*reference lock period in picoseconds*/
uint32_t clock_period;
/* approximate DMTD phase value (on slave port) at which RX timestamp
* (T2) counter transistion occurs (picoseconds) */
uint32_t t2_phase_transition;
/* approximate phase value (on master port) at which RX timestamp (T4)
* counter transistion occurs (picoseconds) */
uint32_t t4_phase_transition;
/* Endpoint's base address */
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 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)
{
return (state != HAL_PORT_STATE_LINK_DOWN
&& state != HAL_PORT_STATE_DISABLED);
}
static inline struct hal_port_state *hal_lookup_port(
struct hal_port_state *ports, int nports,
const char *name)
{
int i;
for (i = 0; i < nports; i++)
if (ports[i].in_use && (!strcmp(name, ports[i].name)))
return ports + i;
return NULL;
}
#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 {
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 */
double alpha;
int delta_tx_ps; /* "delta" of this SFP type WRT calibration type */
int delta_rx_ps;
/* wavelengths, used to get alpha from fiber type */
int tx_wl;
int rx_wl;
/* and link as a list */
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 dot-config to internal structures */
int shw_sfp_read_db(void);
/* 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,
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);
#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-%i"
#define WRS_SHM_MIN_SIZE (4*1024)
#define WRS_SHM_MAX_SIZE (256*1024)
/* Each process "name" (i.e. id) is added to the filename above */
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 fd; /* So we can enlarge it using fd */
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
#define WRS_SHM_LOCKED 0x0002 /* at init time: writers locks, readers wait */
/* 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 */
#define WRS_SHM_WRITE_BEGIN 1
#define WRS_SHM_WRITE_END 0
extern void wrs_shm_write(void *headptr, int flags);
/* 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__ */
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
#ifdef CONFIG_EXT_WR
# define BUILT_WITH_WHITERABBIT 1
#else
# define BUILT_WITH_WHITERABBIT 0
#endif
/*
* These are the functions provided by the various wrs files
*/
#include <minipc.h>
#include <libwr/shmem.h>
#include <libwr/hal_shmem.h>
extern struct minipc_ch *hal_ch;
extern struct minipc_ch *ppsi_ch;
extern struct hal_port_state *hal_ports;
extern int hal_nports;
static inline struct hal_port_state *pp_wrs_lookup_port(char *name)
{
int i;
for (i = 0; i < hal_nports; i++)
if (hal_ports[i].in_use &&!strcmp(name, hal_ports[i].name))
return hal_ports + i;
return NULL;
}
#define DEFAULT_TO 200000 /* ms */
/* FIXME return values, here copied from proto-ext-whiterabbit.
* I do not include proto-ext-whiterabbit/wr-constants.h in order not to
* have a dependency on ext when compiling wrs architecture. All the return
* values mechanism of wrs hw should be reviewed in this src and in the
* whole ppsi */
/* White Rabbit softpll status values */
#define WR_SPLL_OK 0
#define WR_SPLL_READY 1
#define WR_SPLL_ERROR -1
/* White Rabbit calibration defines */
#define WR_HW_CALIB_TX 1
#define WR_HW_CALIB_RX 2
#define WR_HW_CALIB_OK 0
#define WR_HW_CALIB_READY 1
#define WR_HW_CALIB_ERROR -1
#define WR_HW_CALIB_NOT_FOUND -3
#define POSIX_ARCH(ppg) ((struct unix_arch_data *)(ppg->arch_data))
struct unix_arch_data {
struct timeval tv;
};
extern void wrs_main_loop(struct pp_globals *ppg);
extern void wrs_init_ipcserver(struct minipc_ch *ppsi_ch);
/* wrs-calibration.c */
int wrs_read_calibration_data(struct pp_instance *ppi,
uint32_t *delta_tx, uint32_t *delta_rx,
int32_t *fix_alpha, int32_t *clock_period);
int wrs_calibrating_disable(struct pp_instance *ppi, int txrx);
int wrs_calibrating_enable(struct pp_instance *ppi, int txrx);
int wrs_calibrating_poll(struct pp_instance *ppi, int txrx, uint32_t *delta);
int wrs_calibration_pattern_enable(struct pp_instance *ppi,
unsigned int calib_period,
unsigned int calib_pattern,
unsigned int calib_pattern_len);
int wrs_calibration_pattern_disable(struct pp_instance *ppi);
/* wrs-time.c (some should moce to wrs-spll.c) */
int wrs_locking_enable(struct pp_instance *ppi);
int wrs_locking_poll(struct pp_instance *ppi, int grandmaster);
int wrs_locking_disable(struct pp_instance *ppi);
int wrs_enable_ptracker(struct pp_instance *ppi);
int wrs_adjust_in_progress(void);
int wrs_adjust_counters(int64_t adjust_sec, int32_t adjust_nsec);
int wrs_adjust_phase(int32_t phase_ps);
int wrs_enable_timing_output(struct pp_instance *ppi, int enable);
/*
* Copyright (C) 2011 CERN (www.cern.ch)
* Author: Alessandro Rubini
*
* Released to the public domain
*/
/*
* This is the main loop for the wr-switch architecture. It's amost
* the same as the unix main loop, but we must serve RPC calls too
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>
#include <linux/if_ether.h>
#include <ppsi/ppsi.h>
#include <ppsi-wrs.h>
#include <wr-api.h>
#include <hal_exports.h>
/* Call pp_state_machine for each instance. To be called periodically,
* when no packets are incoming */
static int run_all_state_machines(struct pp_globals *ppg)
{
int j;
int delay_ms = 0, delay_ms_j;
for (j = 0; j < ppg->nlinks; j++) {
struct pp_instance *ppi = INST(ppg, j);
int old_lu = WR_DSPOR(ppi)->linkUP;
struct hal_port_state *p;
/* FIXME: we should save this pointer in the ppi itself */
p = pp_wrs_lookup_port(ppi->iface_name);
if (!p) {
fprintf(stderr, "ppsi: can't find %s in shmem\n",
ppi->iface_name);
continue;
}
WR_DSPOR(ppi)->linkUP =
(p->state != HAL_PORT_STATE_LINK_DOWN &&
p->state != HAL_PORT_STATE_DISABLED);
if (old_lu != WR_DSPOR(ppi)->linkUP) {
pp_diag(ppi, fsm, 1, "iface %s went %s\n",
ppi->iface_name, WR_DSPOR(ppi)->linkUP ? "up":"down");
if (WR_DSPOR(ppi)->linkUP) {
ppi->state = PPS_INITIALIZING;
}
else {
ppi->n_ops->exit(ppi);
ppi->frgn_rec_num = 0;
ppi->frgn_rec_best = -1;
if (BUILT_WITH_WHITERABBIT
&& ppg->ebest_idx == ppi->port_idx)
wr_servo_reset();
}
}
/* Do not call state machine if link is down */
if (WR_DSPOR(ppi)->linkUP)
delay_ms_j = pp_state_machine(ppi, NULL, 0);
else
delay_ms_j = PP_DEFAULT_NEXT_DELAY_MS;
/* delay_ms is the least delay_ms among all instances */
if (j == 0)
delay_ms = delay_ms_j;
if (delay_ms_j < delay_ms)
delay_ms = delay_ms_j;
}
return delay_ms;
}
void wrs_main_loop(struct pp_globals *ppg)
{
struct pp_instance *ppi;
int delay_ms;
int j;
/* Initialize each link's state machine */
for (j = 0; j < ppg->nlinks; j++) {
ppi = INST(ppg, j);
/*
* If we are sending or receiving raw ethernet frames,
* the ptp payload is one-eth-header bytes into the frame
*/
if (ppi->proto == PPSI_PROTO_RAW)
NP(ppi)->ptp_offset = ETH_HLEN;
/*
* The main loop here is based on select. While we are not
* doing anything else but the protocol, this allows extra stuff
* to fit.
*/
ppi->is_new_state = 1;
}
delay_ms = run_all_state_machines(ppg);
while (1) {
int i;
minipc_server_action(ppsi_ch, 10 /* ms */);
/*
* If Ebest was changed in previous loop, run best
* master clock before checking for new packets, which
* would affect port state again
*/
if (ppg->ebest_updated) {
for (j = 0; j < ppg->nlinks; j++) {
int new_state;
struct pp_instance *ppi = INST(ppg, j);
new_state = bmc(ppi);
if (new_state != ppi->state) {
ppi->state = new_state;
ppi->is_new_state = 1;
}
}
ppg->ebest_updated = 0;
}
i = wrs_net_ops.check_packet(ppg, delay_ms);
if (i < 0)
continue;
if (i == 0) {
delay_ms = run_all_state_machines(ppg);
continue;
}
/* If delay_ms is -1, the above ops.check_packet will continue
* consuming the previous timeout (see its implementation).
* This ensures that every state machine is called at least once
* every delay_ms */
delay_ms = -1;
for (j = 0; j < ppg->nlinks; j++) {
int tmp_d;
ppi = INST(ppg, j);
if ((NP(ppi)->ch[PP_NP_GEN].pkt_present) ||
(NP(ppi)->ch[PP_NP_EVT].pkt_present)) {
i = ppi->n_ops->recv(ppi, ppi->rx_frame,
PP_MAX_FRAME_LENGTH - 4,
&ppi->last_rcv_time);
if (i == -2) {
continue; /* dropped */
}
if (i == -1) {
pp_diag(ppi, frames, 1,
"Receive Error %i: %s\n",
errno, strerror(errno));
continue;
}
if (i < PP_MINIMUM_LENGTH) {
pp_diag(ppi, frames, 1,
"Short frame: %d < %d\n", i,
PP_MINIMUM_LENGTH);
continue;
}
tmp_d = pp_state_machine(ppi, ppi->rx_ptp,
i - NP(ppi)->ptp_offset);
if ((delay_ms == -1) || (tmp_d < delay_ms))
delay_ms = tmp_d;
}
}
}
}
This diff is collapsed.
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -O2 -fno-strict-aliasing
LDFLAGS = -L. -lminipc -lm
# We need to support freestading environments: an embedded CPU that
# sits as a server on its own memory are and awaits commands
# The user can pass IPC_FREESTANDING=y
IPC_FREESTANDING ?= $(shell ./check-freestanding $(CC))
IPC_HOSTED ?= $(shell ./check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -Iarch-$(ARCH)
else
IPC_HOSTED = y
endif
OBJ-$(IPC_HOSTED) = minipc-core.o minipc-server.o minipc-client.o
OBJ-$(IPC_FREESTANDING) = minipc-mem-server.o
LIB = libminipc.a
# export these to the examples (but if you make there IPC_HOSTED is default
export IPC_FREESTANDING IPC_HOSTED
all: $(LIB)
$(MAKE) -C examples
$(LIB): $(OBJ-y)
$(AR) r $@ $^
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(wildcard *.o): $(wildcard *.h)
clean:
rm -f *.o *~ $(LIB)
$(MAKE) -C examples clean
install:
@echo "We have no install rule by now"
All documentation is now under the doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
#ifndef __ARCH_LM32_STDINT_H__
#define __ARCH_LM32_STDINT_H__
/*
* We miss a stdint.h in our compiler, so provide some types here,
* knowing the CPU is 32-bits and uses LP32 model
*/
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int32_t;
#endif /* __ARCH_LM32_STDINT_H__ */
#!/bin/sh
# I used to just check __STDC_HOSTED__ without special CFLAGS, but now I
# want to force freestanding compilation on otherwise hosted processors.
# Thus, the user can set IPC_FREESTANDING=y . If unset, this gets called.
# Accept "-n" to invert the result (select second field, not first)
if [ "x$1" = "x-n" ]; then select=2; shift; else select=1; fi
CC=$1
if [ "x$CC" = "x" ]; then
echo "$0: pass the compiler path (\$CC) as argument" >& 2
exit 1
fi
# Check the compiler: if it has no matches is prints the unadorned string
if [ "$($CC -print-file-name=libc.so)" = "libc.so" ]; then
res=y,n
else
res=n,y
fi
# If passed from the user, override the autodetected value
if [ "$IPC_FREESTANDING" = "y" ]; then
res=y,n
fi
echo $res | cut -d, -f $select
*~
*.aux
*.cp
*.fn
*.html
*.info
*.ky
*.log
*.pdf
*.pg
*.texi
*.toc
*.tp
*.txt
*.vr
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
%.texi: %.in
@rm -f $@
sed -f ./infofilter $< > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
install:
;; use:
;; emacs -batch -l ./fixinfo.el <file>
;; or, better:
;; emacs -batch --no-site-file -l ./fixinfo.el <file>
(defun fixinfo (file)
(find-file-other-window file)
(message (concat "Maxing texinfo tree in " file))
(texinfo-all-menus-update)
(texinfo-every-node-update)
(save-buffer)
(kill-buffer (current-buffer))
)
;; loop over command line arguments
(mapcar 'fixinfo command-line-args-left)
(kill-emacs)
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
#preserve blanks and braces in @example blocks
/@example/,/@end example/ s/{/@{/g
/@example/,/@end example/ s/}/@}/g
/@example/,/@end example/ p
/@example/,/@end example/ d
/@smallexample/,/@end smallexample/ s/{/@{/g
/@smallexample/,/@end smallexample/ s/}/@}/g
/@smallexample/,/@end smallexample/ p
/@smallexample/,/@end smallexample/ d
# remove leading blanks
s/^[ ]*//
This diff is collapsed.
trivial-server
trivial-client
pty-server
pty-client
mbox-bridge
mbox-client
mbox-process
shmem-server
shmem-client
freestanding-server
# examples for mini-rpc; depends on ..
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb -I.. -O2
LDFLAGS = -L.. -lminipc -lm
# we may be hosted or freestanding. For freestanding there is one
# example only. The following variables are exported by the upper
# Makefile, but if you "make" in this directory, it checks by itself
IPC_FREESTANDING ?= $(shell ../check-freestanding $(CC))
IPC_HOSTED ?= $(shell ../check-freestanding -n $(CC))
# Hosted is the opposite of freestanding, and cflags change too
ifeq ($(IPC_FREESTANDING),y)
IPC_HOSTED = n
CFLAGS += -ffreestanding -I../arch-$(ARCH)
else
IPC_HOSTED = y
endif
PROGS-$(IPC_HOSTED) = trivial-server trivial-client
PROGS-$(IPC_HOSTED) += pty-server pty-client
PROGS-$(IPC_HOSTED) += mbox-process mbox-bridge mbox-client
PROGS-$(IPC_HOSTED) += shmem-server shmem-client
IPC_FREESTANDING ?= n
PROGS-$(IPC_FREESTANDING) += freestanding-server
all: $(PROGS-y)
# the default puts LDFLAGS too early. Bah...
%: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
pty-server: pty-server.o pty-rpc_server.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -lutil -o $@
pty-client: pty-client.o pty-rpc_structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-server: shmem-server.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
shmem-client: shmem-client.o shmem-structs.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
# This is stupid, it won't apply the first time, but, well... it works
$(PROGS-y) $(wildcard *.o): $(wildcard ../*.h ../*.a)
clean:
rm -f *.o *~ $(PROGS)
All documentation is now under the ../doc/ directory.
The input file is called doc/mini-ipc.in (it is readable by itself).
To have all usual output formats you can run "make" withing ./doc
but you need TexInfo installed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#include "minipc.h"
struct pty_counts {
int in, out;
};
#define PTY_RPC_NAME "pty-server"
/* structures are in pty-rpc_structs.c */
extern struct minipc_pd
rpc_count, rpc_getenv, rpc_setenv, rpc_feed, rpc_strlen, rpc_strcat, rpc_stat;
/* The server needs to call out to a different file */
extern int pty_export_functions(struct minipc_ch *ch,
int fdm, struct pty_counts *pc);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment