Commit 79672157 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

software: fdelay library: remote operation via Ethernet

parent 3e17d062
CFLAGS = -I/home/slayer/rr/gnurabbit/kernel -Iinclude -g
OBJS = fdelay_lib.o fdelay_test.o rr_io.o
all: $(OBJS)
gcc -o fd_test $(OBJS) -lm
clean:
rm -f fd_test $(OBJS)
\ No newline at end of file
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
User-space driver/library
Private includes
Tomasz Włostowski/BE-CO-HT, 2011
(c) Copyright CERN 2011
Licensed under LGPL 2.1
*/
#ifndef __FDELAY_PRIVATE_H
#define __FDELAY_PRIVATE_H
#include <stdint.h>
/* SPI Bus chip selects */
#define CS_PLL 1 /* AD9516 PLL */
#define CS_GPIO 2 /* MCP23S17 GPIO */
/* MCP23S17 GPIO expander pin locations: bit 8 = select bank 2, bits 7..0 = mask of the pin in the selected bank */
#define SGPIO_TERM_EN (0x100 | (1<<7)) /* Input termination enable (1 = on) */
#define SGPIO_LED_TERM (0x100 | (1<<2)) /* Termination enable LED (1 = on) */
#define SGPIO_DRV_OEN (0x100 | (1<<0)) /* Output driver enable (0 = on) */
#define SGPIO_TRIG_SEL (0x100 | (1<<3)) /* TDC trigger select (0 = trigger input, 1 = FPGA) */
/* ACAM TDC operation modes */
#define ACAM_RMODE 0
#define ACAM_IMODE 1
/* MCP23S17 register addresses (only ones which are used by the lib) */
#define MCP_IODIR 0x0
#define MCP_GPIO 0x12
#define MCP_IOCON 0x0a
/* Number of fractional bits in the timestamps/time definitions. Must be consistent with the HDL bitstream. */
#define FDELAY_FRAC_BITS 12
/* Fractional bits shifted away when converting the fine (< 8ns) part to fit the range of SY89295 delay line. */
#define FDELAY_SCALER_SHIFT 12
/* Number of delay line taps */
#define FDELAY_NUM_TAPS 1024
/* How many times each calibration measurement will be averaged */
#define FDELAY_CAL_AVG_STEPS 1024
/* Fine Delay Card Magic ID */
#define FDELAY_MAGIC_ID 0xf19ede1a
/* RSTR Register value which triggers a reset of the FD Core */
#define FDELAY_RSTR_TRIGGER 0xdeadbeef
/* Calibration eeprom I2C address */
#define EEPROM_ADDR 0x50
/* ACAM Calibration parameters */
struct fine_delay_calibration {
uint32_t magic; /* magic ID: 0xf19ede1a */
uint32_t zero_offset[4]; /* Output zero offset, in nsec << FDELAY_FRAC_BITS */
uint32_t adsfr_val; /* ADSFR register value */
uint32_t acam_start_offset; /* ACAM Start offset value */
uint32_t atmcr_val; /* ATMCR register value */
int32_t dly_tempco[4]; /* SY89295 delay/temperature coefficient in ps/degC << FDELAY_FRAC_BITS */
int32_t zero_tempco[4]; /* Zero offset/temperature coefficient in ps/degC << FDELAY_FRAC_BITS */
int32_t cal_temp; /* Calibration temperature in 0.1 degC */
uint32_t tdc_zero_offset; /* Zero offset of the TDC, in picoseconds */
} __attribute__((packed));
/* Internal state of the fine delay card */
struct fine_delay_hw
{
uint32_t base_addr; /* Base address of the core */
uint32_t base_onewire; /* Base address of the core */
uint32_t base_i2c; /* SPI Controller offset */
double acam_bin; /* bin size of the ACAM TDC - calculated for */
uint32_t frr[4]; /* Fine range register for each output, determi*/
int32_t board_temp; /* Current temperature of the board in 0.1 degC */
int wr_enabled;
int wr_state;
struct fine_delay_calibration calib;
};
/* some useful access/declaration macros */
#define fd_writel(data, addr) dev->writel(dev->priv_io, data, (dev->base_addr + (addr)))
#define fd_readl(addr) dev->readl(dev->priv_io, (dev->base_addr + (addr)))
#define fd_decl_private(dev) struct fine_delay_hw *hw = (struct fine_delay_hw *) dev->priv_fd;
#endif
#ifndef __I2C_MASTER_H
#define __I2C_MASTER_H
#include <stdint.h>
#include "fdelay_lib.h"
void mi2c_init(fdelay_device_t *dev);
int eeprom_read(fdelay_device_t *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size);
int eeprom_write(fdelay_device_t *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size);
#endif
#ifndef __OPENCORES_SPI_H
#define __OPENCORES_SPI_H
#include <stdint.h>
#define OCSPI_REG_RX0 0
#define OCSPI_REG_RX1 4
#define OCSPI_REG_RX2 8
#define OCSPI_REG_RX3 12
#define OCSPI_REG_TX0 0
#define OCSPI_REG_TX1 4
#define OCSPI_REG_TX2 8
#define OCSPI_REG_TX3 12
#define OCSPI_REG_CTRL 16
#define OCSPI_REG_DIVIDER 20
#define OCSPI_REG_SS 24
#define OCSPI_CTRL_ASS (1<<13)
#define OCSPI_CTRL_IE (1<<12)
#define OCSPI_CTRL_LSB (1<<11)
#define OCSPI_CTRL_TXNEG (1<<10)
#define OCSPI_CTRL_RXNEG (1<<9)
#define OCSPI_CTRL_GO_BSY (1<<8)
#define OCSPI_CTRL_CHAR_LEN(x) ((x) & 0x7f)
#endif
/*
* Public header for the raw I/O interface for PCI or PCI express interfaces
*
* Copyright (C) 2010 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#ifndef __RAWRABBIT_H__
#define __RAWRABBIT_H__
#include <linux/types.h>
#include <linux/ioctl.h>
#ifdef __KERNEL__ /* The initial part of the file is driver-internal stuff */
#include <linux/pci.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <linux/wait.h>
#include <linux/completion.h>
struct rr_devsel;
struct rr_dev {
struct rr_devsel *devsel;
struct pci_driver *pci_driver;
struct pci_device_id *id_table;
struct pci_dev *pdev; /* non-null after pciprobe */
struct mutex mutex;
wait_queue_head_t q;
void *dmabuf;
char *fwname;
struct timespec irqtime;
unsigned long irqcount;
struct completion complete;
struct resource *area[3]; /* bar 0, 2, 4 */
void *remap[3]; /* ioremap of bar 0, 2, 4 */
unsigned long flags;
struct work_struct work;
const struct firmware *fw;
struct completion fw_load;
void (*load_program)(struct rr_dev *); /* lm32 */
int usecount;
#ifdef IS_SPEC_DEMO
struct miscdevice misc;
char miscname[32]; /* "spec-demo-<bus>-<slot> */
struct list_head list;
#endif
};
extern char *rr_fwname; /* module parameter. If "" then defaults apply */
#define RR_FLAG_REGISTERED 0x00000001
#define RR_FLAG_IRQDISABLE 0x00000002
#define RR_FLAG_IRQREQUEST 0x00000002
#define RR_PROBE_TIMEOUT (HZ) /* for pci_register_drv */
/* These two live in ./loader.c */
extern void rr_ask_firmware(struct rr_dev *dev);
extern void rr_load_firmware(struct work_struct *work);
/* And, for the spec only, this is in ./spec-loader.c */
extern void spec_ask_program(struct rr_dev *dev);
#endif /* __KERNEL__ */
/* By default, the driver registers for this vendor/devid */
#define RR_DEFAULT_VENDOR 0x1a39
#define RR_DEFAULT_DEVICE 0x0004
#define RR_DEFAULT_FWNAME "rrabbit-%P-%p@%b"
#define RR_MAX_FWNAME_SIZE 64
#define RR_DEFAULT_BUFSIZE (1<<20) /* 1MB */
#define RR_PLIST_SIZE 4096 /* no PAGE_SIZE in user space */
#define RR_PLIST_LEN (RR_PLIST_SIZE / sizeof(void *))
#define RR_MAX_BUFSIZE (RR_PLIST_SIZE * RR_PLIST_LEN)
/* This structure is used to select the device to be accessed, via ioctl */
struct rr_devsel {
__u16 vendor;
__u16 device;
__u16 subvendor; /* RR_DEVSEL_UNUSED to ignore subvendor/dev */
__u16 subdevice;
__u16 bus; /* RR_DEVSEL_UNUSED to ignore bus and devfn */
__u16 devfn;
};
#define RR_DEVSEL_UNUSED 0xffff
/* Offsets for BAR areas in llseek() and/or ioctl */
#define RR_BAR_0 0x00000000
#define RR_BAR_2 0x20000000
#define RR_BAR_4 0x40000000
#define RR_BAR_BUF 0xc0000000 /* The DMA buffer */
#define RR_IS_DMABUF(addr) ((addr) >= RR_BAR_BUF)
#define __RR_GET_BAR(x) ((x) >> 28)
#define __RR_SET_BAR(x) ((x) << 28)
#define __RR_GET_OFF(x) ((x) & 0x0fffffff)
static inline int rr_is_valid_bar(unsigned long address)
{
int bar = __RR_GET_BAR(address);
return bar == 0 || bar == 2 || bar == 4 || bar == 0x0c;
}
static inline int rr_is_dmabuf_bar(unsigned long address)
{
int bar = __RR_GET_BAR(address);
return bar == 0x0c;
}
struct rr_iocmd {
__u32 address; /* bar and offset */
__u32 datasize; /* 1 or 2 or 4 or 8 */
union {
__u8 data8;
__u16 data16;
__u32 data32;
__u64 data64;
};
};
/* ioctl commands */
#define __RR_IOC_MAGIC '4' /* random or so */
#define RR_DEVSEL _IOW(__RR_IOC_MAGIC, 0, struct rr_devsel)
#define RR_DEVGET _IOR(__RR_IOC_MAGIC, 1, struct rr_devsel)
#define RR_READ _IOWR(__RR_IOC_MAGIC, 2, struct rr_iocmd)
#define RR_WRITE _IOW(__RR_IOC_MAGIC, 3, struct rr_iocmd)
#define RR_IRQWAIT _IO(__RR_IOC_MAGIC, 4)
#define RR_IRQENA _IO(__RR_IOC_MAGIC, 5)
#define RR_GETDMASIZE _IO(__RR_IOC_MAGIC, 6)
/* #define RR_SETDMASIZE _IO(__RR_IOC_MAGIC, 7, unsigned long) */
#define RR_GETPLIST _IO(__RR_IOC_MAGIC, 8) /* returns a whole page */
#define VFAT_IOCTL_READDIR_BOTH _IOR('r', 1, struct dirent [2])
/* Registers from the gennum header files */
enum {
GNGPIO_BASE = 0xA00,
GNGPIO_DIRECTION_MODE = GNGPIO_BASE + 0x4,
GNGPIO_OUTPUT_ENABLE = GNGPIO_BASE + 0x8,
GNGPIO_OUTPUT_VALUE = GNGPIO_BASE + 0xC,
GNGPIO_INPUT_VALUE = GNGPIO_BASE + 0x10,
FCL_BASE = 0xB00,
FCL_CTRL = FCL_BASE,
FCL_STATUS = FCL_BASE + 0x4,
FCL_IODATA_IN = FCL_BASE + 0x8,
FCL_IODATA_OUT = FCL_BASE + 0xC,
FCL_EN = FCL_BASE + 0x10,
FCL_TIMER_0 = FCL_BASE + 0x14,
FCL_TIMER_1 = FCL_BASE + 0x18,
FCL_CLK_DIV = FCL_BASE + 0x1C,
FCL_IRQ = FCL_BASE + 0x20,
FCL_TIMER_CTRL = FCL_BASE + 0x24,
FCL_IM = FCL_BASE + 0x28,
FCL_TIMER2_0 = FCL_BASE + 0x2C,
FCL_TIMER2_1 = FCL_BASE + 0x30,
FCL_DBG_STS = FCL_BASE + 0x34,
FCL_FIFO = 0xE00,
PCI_SYS_CFG_SYSTEM = 0x800
};
#endif /* __RAWRABBIT_H__ */
CFLAGS = -I../include -g -Imini_bone
OBJS_LIB = fdelay_lib.o rr_io.o i2c_master.o mini_bone/minibone_lib.o mini_bone/ptpd_netif.o fdelay_bus.o
all: $(OBJS_LIB)
gcc -shared -o libfinedelay.so $(OBJS_LIB)
clean:
rm -f libfinedelay.so $(OBJS_LIB)
\ No newline at end of file
/*
FmcDelay1ns4Cha (a.k.a. The Fine Delay Card)
User-space driver/library - bus API creation functions
Tomasz Włostowski/BE-CO-HT, 2011
(c) Copyright CERN 2011
Licensed under LGPL 2.1
*/
#include <stdio.h>
#include <stdlib.h>
#include "rr_io.h"
#include "minibone_lib.h"
#include "fdelay_lib.h"
static void my_rr_writel(void *priv, uint32_t data, uint32_t addr)
{
rr_writel(data, addr);
}
static uint32_t my_rr_readl(void *priv, uint32_t addr)
{
uint32_t d = rr_readl(addr);
return d;
}
static void my_mb_writel(void *priv, uint32_t data, uint32_t addr)
{
mbn_writel(priv, data, addr >> 2);
}
static uint32_t my_mb_readl(void *priv, uint32_t addr)
{
uint32_t d = mbn_readl(priv, addr >> 2);
return d;
}
fdelay_device_t *fdelay_create_rawrabbit(uint32_t base_addr)
{
fdelay_device_t *dev = malloc(sizeof(fdelay_device_t));
rr_init();
dev->writel = my_rr_writel;
dev->readl = my_rr_readl;
dev->base_addr = base_addr;
return dev;
}
fdelay_device_t *fdelay_create_minibone(char *iface, char *mac_addr, uint32_t base_addr)
{
void *handle;
uint8_t target_mac[6];
fdelay_device_t *dev = malloc(sizeof(fdelay_device_t));
sscanf(mac_addr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &target_mac[0], &target_mac[1], &target_mac[2], &target_mac[3], &target_mac[4], &target_mac[5]);
handle = mbn_open(iface, target_mac);
if(handle == NULL)
return NULL;
// dbg("%s: remote @ %s [%02x:%02x:%02x:%02x:%02x:%02x], base 0x%08x\n",__FUNCTION__, iface,
// target_mac[0], target_mac[1], target_mac[2], target_mac[3], target_mac[4], target_mac[5], base_addr);
dev->writel = my_mb_writel;
dev->readl = my_mb_readl;
dev->base_addr = base_addr;
dev->priv_io = handle;
return dev;
}
#include <stdio.h>
#include "fdelay_lib.h"
#include "fdelay_private.h"
#include "fdelay_regs.h"
#define M_SDA_OUT(x) { \
if(x) \
fd_writel(fd_readl(FD_REG_I2CR) | FD_I2CR_SDA_OUT, FD_REG_I2CR); \
else \
fd_writel(fd_readl(FD_REG_I2CR) & (~FD_I2CR_SDA_OUT), FD_REG_I2CR); \
udelay(10);\
}
#define M_SCL_OUT(x) { \
if(x) \
fd_writel(fd_readl(FD_REG_I2CR) | FD_I2CR_SCL_OUT, FD_REG_I2CR); \
else \
fd_writel(fd_readl(FD_REG_I2CR) & (~FD_I2CR_SCL_OUT), FD_REG_I2CR); \
udelay(10); \
}
#define M_SDA_IN ((fd_readl(FD_REG_I2CR) & FD_I2CR_SDA_IN) ? 1 : 0)
static void mi2c_start(fdelay_device_t *dev)
{
fd_decl_private(dev);
M_SDA_OUT(0);
M_SCL_OUT(0);
}
static void mi2c_repeat_start(fdelay_device_t *dev)
{
fd_decl_private(dev);
M_SDA_OUT(1);
M_SCL_OUT(1);
M_SDA_OUT(0);
M_SCL_OUT(0);
}
static void mi2c_stop(fdelay_device_t *dev)
{
fd_decl_private(dev);
M_SDA_OUT(0);
M_SCL_OUT(1);
M_SDA_OUT(1);
}
int mi2c_put_byte(fdelay_device_t *dev, unsigned char data)
{
fd_decl_private(dev);
char i;
unsigned char ack;
for (i=0;i<8;i++, data<<=1)
{
M_SDA_OUT(data&0x80);
M_SCL_OUT(1);
M_SCL_OUT(0);
}
M_SDA_OUT(1);
M_SCL_OUT(1);
ack = M_SDA_IN; /* ack: sda is pulled low ->success. */
M_SCL_OUT(0);
M_SDA_OUT(0);
return ack!=0 ? -1 : 0;
}
void mi2c_get_byte(fdelay_device_t *dev, unsigned char *data, int ack)
{
int i;
unsigned char indata = 0;
/* assert: scl is low */
M_SCL_OUT(0);
M_SDA_OUT(1);
for (i=0;i<8;i++)
{
M_SCL_OUT(1);
indata <<= 1;
if ( M_SDA_IN ) indata |= 0x01;
M_SCL_OUT(0);
}
M_SDA_OUT((ack ? 0 : 1));
M_SCL_OUT(1);
M_SCL_OUT(0);
M_SDA_OUT(0);
*data= indata;
}
void mi2c_init(fdelay_device_t *dev)
{
fd_decl_private(dev);
M_SCL_OUT(1);
M_SDA_OUT(1);
}
void mi2c_scan(fdelay_device_t *dev)
{
int i;
for(i=0;i<256;i+=2)
{
mi2c_start(dev);
if(!mi2c_put_byte(dev,i))
printf("Found device at 0x%x\n", i>>1);
mi2c_stop(dev);
}
}
int eeprom_read(fdelay_device_t *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
{
int i;
unsigned char c;
for(i=0;i<size;i++)
{
mi2c_start(dev);
if(mi2c_put_byte(dev, i2c_addr << 1) < 0)
{
mi2c_stop(dev);
return -1;
}
mi2c_put_byte(dev, (offset >> 8) & 0xff);
mi2c_put_byte(dev, offset & 0xff);
offset++;
mi2c_stop(dev);
mi2c_start(dev);
mi2c_put_byte(dev, (i2c_addr << 1) | 1);
mi2c_get_byte(dev, &c, 0);
printf("readback: %x\n", c);
*buf++ = c;
mi2c_stop(dev);
}
return size;
}
int eeprom_write(fdelay_device_t *dev, uint8_t i2c_addr, uint32_t offset, uint8_t *buf, size_t size)
{
int i, busy;
for(i=0;i<size;i++)
{
mi2c_start(dev);
if(mi2c_put_byte(dev, i2c_addr << 1) < 0)
{
mi2c_stop(dev);
return -1;
}
mi2c_put_byte(dev, (offset >> 8) & 0xff);
mi2c_put_byte(dev, offset & 0xff);
mi2c_put_byte(dev, *buf++);
offset++;
mi2c_stop(dev);
do /* wait until the chip becomes ready */
{
mi2c_start(dev);
busy = mi2c_put_byte(dev, i2c_addr << 1);
mi2c_stop(dev);
} while(busy);
}
return size;
}
\ No newline at end of file
OBJS = minibone_lib.o ptpd_netif.o
all: $(OBJS)
gcc -o m $(OBJS)
\ No newline at end of file
/* MiniBone library. BUGGY CRAP CODE INTENDED FOR TESTING ONLY! */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/time.h>
#include "ptpd_netif.h"
#define F_SEL(x) (x & 0xf)
#define F_ERROR (1<<1)
#define F_READBACK (1<<0)
#define F_WRITE (1<<4)
#define RX_TIMEOUT 10
#define MBN_ETHERTYPE 0xa0a0
struct mbn_packet {
uint16_t flags ;
uint32_t a_d;
uint32_t d;
} __attribute__((packed));
struct mb_device {
mac_addr_t dest;
uint16_t ethertype;
wr_socket_t *sock;
int tx_packets, rx_packets, tx_retries, rx_retries;
};
typedef struct
{
uint64_t start_tics;
uint64_t timeout;
} timeout_t ;
static uint64_t get_tics()
{
struct timezone tz = {0, 0};
struct timeval tv;
gettimeofday(&tv, &tz);
return (uint64_t) tv.tv_sec * 1000000ULL + (uint64_t) tv.tv_usec;
}
static inline int tmo_init(timeout_t *tmo, uint32_t milliseconds)
{
tmo->start_tics = get_tics();
tmo->timeout = (uint64_t) milliseconds * 1000ULL;
return 0;
}
static inline int tmo_restart(timeout_t *tmo)
{
tmo->start_tics = get_tics();
return 0;
}
static inline int tmo_expired(timeout_t *tmo)
{
return (get_tics() - tmo->start_tics > tmo->timeout);
}
void *mbn_open(const char *if_name, mac_addr_t target)
{
struct mb_device *dev = malloc(sizeof(struct mb_device));
wr_sockaddr_t saddr;
if(!dev)
return NULL;
memset(dev, 0, sizeof(struct mb_device));
memcpy(dev->dest, target, 6);
strcpy(saddr.if_name, if_name);
memcpy(saddr.mac, target, 6);
saddr.ethertype = htons(MBN_ETHERTYPE);
saddr.family = PTPD_SOCK_RAW_ETHERNET;
dev->sock = ptpd_netif_create_socket(PTPD_SOCK_RAW_ETHERNET, 0, &saddr);
if(!dev->sock)
{
free(dev);
return NULL;
}
return (void *)dev;
}
static int mbn_send(void *priv, uint8_t *data, int size)
{
struct mb_device *dev = (struct mb_device *)priv;
wr_sockaddr_t to;
memcpy(to.mac, dev->dest, 6);
to.ethertype = MBN_ETHERTYPE;
return ptpd_netif_sendto(dev->sock, &to, (void*)data, size, NULL);
}
static int mbn_recv(void *handle, uint8_t *data, int size, int timeout)
{
struct mb_device *dev = (struct mb_device *)handle;
wr_sockaddr_t from;
timeout_t rx_tmo;
tmo_init(&rx_tmo, timeout);
do {
int n = ptpd_netif_recvfrom(dev->sock, &from, (void*)data, size, NULL);
if(n > 0 && from.ethertype == MBN_ETHERTYPE && !memcmp(from.mac, dev->dest, 6))
{
dev->rx_packets++;
return n;
}
// dev->rx_retries++;
} while(!tmo_expired(&rx_tmo));
return 0;
}
void mbn_writel(void *handle, uint32_t d, uint32_t a)
{
struct mb_device *dev = (struct mb_device *)handle;
int n_retries = 3;
struct mbn_packet pkt;
while(n_retries--)
{
pkt.flags = htons(F_SEL(0xf) | F_WRITE);
pkt.a_d= htonl(a);
pkt.d=htonl(d);
mbn_send(handle, (uint8_t *)&pkt, sizeof(pkt));
int n = mbn_recv(handle, (uint8_t *)&pkt, sizeof(pkt), RX_TIMEOUT);
pkt.flags = ntohs(pkt.flags);
if(n == sizeof(pkt) && ! (!(pkt.flags && F_READBACK) && !(pkt.flags & F_ERROR)))
{
int i;
fprintf(stderr,"\nBadPacket: ");
for(i=0;i<n; i++) fprintf(stderr,"%02x ", *(uint8_t*) (&pkt + i));
fprintf(stderr,"\n");
} if(n == sizeof(pkt) && !(pkt.flags && F_READBACK) && !(pkt.flags & F_ERROR))
{
int i;
// fprintf(stderr,"GoodFlags: %x\n", pkt.flags);
/*fprintf(stderr,"\nGoodPacket: ");
for(i=0;i<n; i++) fprintf(stderr,"%02x ", *(uint8_t*) (&pkt + i));
fprintf(stderr,"\n");*/
dev->tx_packets++;
return ;
}
dev->tx_retries++;
}
fprintf(stderr, "No ack.\n");
}
uint32_t mbn_readl(void *handle, uint32_t a)
{
int n_retries = 3;
struct mb_device *dev = (struct mb_device *)handle;
struct mbn_packet pkt;
pkt.flags = htons(F_SEL(0xf));
pkt.a_d= htonl(a);
while(n_retries--)
{
mbn_send(handle, (uint8_t *)&pkt, sizeof(pkt));
int n = mbn_recv(handle, (uint8_t *)&pkt, sizeof(pkt), RX_TIMEOUT);
pkt.flags = ntohs(pkt.flags);
if(n == sizeof(pkt) && (pkt.flags & F_READBACK) && !(pkt.flags & F_ERROR))
{
return ntohl(pkt.a_d);
}
dev->tx_retries++;
}
fprintf(stderr, "No ack.\n");
}
void mbn_stats(void *handle)
{
struct mb_device *dev = (struct mb_device *)handle;
fprintf(stderr,"Sent: %d [retries: %d], rcvd: %d [retries: %d]\n", dev->tx_packets, dev->tx_retries, dev->rx_packets, dev->rx_retries);
}
void mbn_close(void *handle)
{
struct mb_device *dev = (struct mb_device *)handle;
ptpd_netif_close_socket(dev->sock);
}
\ No newline at end of file
/* MiniBone library. BUGGY CRAP CODE INTENDED FOR TESTING ONLY! */
#ifndef __MINIBONE_LIB_H
#define __MINIBONE_LIB_H
#include <stdint.h>
void *mbn_open(const char *if_name, uint8_t target_mac[]);
void mbn_writel(void *handle, uint32_t d, uint32_t a);
uint32_t mbn_readl(void *handle, uint32_t a);
void mbn_close(void *handle);
#endif
// Supports only raw ethernet now.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/errqueue.h>
#include <linux/sockios.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <fcntl.h>
#include <errno.h>
#include <asm/socket.h>
#include "ptpd_netif.h"
#ifdef NETIF_VERBOSE
#define netif_dbg(...) printf(__VA_ARGS__)
#else
#define netif_dbg(...)
#endif
#define ETHER_MTU 1518
#define DMTD_UPDATE_INTERVAL 100
struct scm_timestamping {
struct timespec systime;
struct timespec hwtimetrans;
struct timespec hwtimeraw;
};
PACKED struct etherpacket {
struct ethhdr ether;
char data[ETHER_MTU];
};
struct tx_timestamp {
int valid;
wr_timestamp_t ts;
uint32_t tag;
uint64_t t_acq;
};
struct my_socket {
int fd;
wr_sockaddr_t bind_addr;
mac_addr_t local_mac;
int if_index;
// parameters for linearization of RX timestamps
uint32_t clock_period;
uint32_t phase_transition;
uint32_t dmtd_phase;
};
struct nasty_hack{
char if_name[20];
int clockedAsPrimary;
};
#ifdef MACIEK_HACKs
struct nasty_hack locking_hack;
#endif
wr_socket_t *ptpd_netif_create_socket(int sock_type, int flags,
wr_sockaddr_t *bind_addr)
{
struct my_socket *s;
struct sockaddr_ll sll;
struct ifreq f;
int fd;
// fprintf(stderr,"CreateSocket!\n");
if(sock_type != PTPD_SOCK_RAW_ETHERNET)
return NULL;
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if(fd < 0)
{
perror("socket()");
return NULL;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
// Put the controller in promiscious mode, so it receives everything
strcpy(f.ifr_name, bind_addr->if_name);
if(ioctl(fd, SIOCGIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }
f.ifr_flags |= IFF_PROMISC;
if(ioctl(fd, SIOCSIFFLAGS,&f) < 0) { perror("ioctl()"); return NULL; }
// Find the inteface index
strcpy(f.ifr_name, bind_addr->if_name);
ioctl(fd, SIOCGIFINDEX, &f);
sll.sll_ifindex = f.ifr_ifindex;
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(bind_addr->ethertype);
sll.sll_halen = 6;
memcpy(sll.sll_addr, bind_addr->mac, 6);
if(bind(fd, (struct sockaddr *)&sll, sizeof(struct sockaddr_ll)) < 0)
{
close(fd);
perror("bind()");
return NULL;
}
s=calloc(sizeof(struct my_socket), 1);
s->if_index = f.ifr_ifindex;
// get interface MAC address
if (ioctl(fd, SIOCGIFHWADDR, &f) < 0) {
perror("ioctl()"); return NULL;
}
memcpy(s->local_mac, f.ifr_hwaddr.sa_data, 6);
memcpy(&s->bind_addr, bind_addr, sizeof(wr_sockaddr_t));
s->fd = fd;
return (wr_socket_t*)s;
}
int ptpd_netif_close_socket(wr_socket_t *sock)
{
struct my_socket *s = (struct my_socket *) sock;
if(!s)
return 0;
close(s->fd);
return 0;
}
int ptpd_netif_sendto(wr_socket_t *sock, wr_sockaddr_t *to, void *data,
size_t data_length, wr_timestamp_t *tx_ts)
{
struct etherpacket pkt;
struct my_socket *s = (struct my_socket *)sock;
struct sockaddr_ll sll;
int rval;
wr_timestamp_t ts;
if(s->bind_addr.family != PTPD_SOCK_RAW_ETHERNET)
return -ENOTSUP;
if(data_length > ETHER_MTU-8) return -EINVAL;
memset(&pkt, 0, sizeof(struct etherpacket));
memcpy(pkt.ether.h_dest, to->mac, 6);
memcpy(pkt.ether.h_source, s->local_mac, 6);
pkt.ether.h_proto =htons(to->ethertype);
memcpy(pkt.data, data, data_length);
size_t len = data_length + sizeof(struct ethhdr);
if(len < 72)
len = 72;
memset(&sll, 0, sizeof(struct sockaddr_ll));
sll.sll_ifindex = s->if_index;
sll.sll_family = AF_PACKET;
sll.sll_protocol = htons(to->ethertype);
sll.sll_halen = 6;
// fprintf(stderr,"fd %d ifi %d ethertype %d\n", s->fd,
// s->if_index, to->ethertype);
rval = sendto(s->fd, &pkt, len, 0, (struct sockaddr *)&sll,
sizeof(struct sockaddr_ll));
return rval;
}
int ptpd_netif_recvfrom(wr_socket_t *sock, wr_sockaddr_t *from, void *data,
size_t data_length, wr_timestamp_t *rx_timestamp)
{
struct my_socket *s = (struct my_socket *)sock;
struct etherpacket pkt;
struct msghdr msg;
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[1024];
} control;
struct cmsghdr *cmsg;
struct scm_timestamping *sts = NULL;
size_t len = data_length + sizeof(struct ethhdr);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = &pkt;
entry.iov_len = len;
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
int ret = recvmsg(s->fd, &msg, MSG_DONTWAIT);
if(ret < 0 && errno==EAGAIN) return 0; // would be blocking
if(ret == -EAGAIN) return 0;
if(ret <= 0) return ret;
memcpy(data, pkt.data, ret - sizeof(struct ethhdr));
from->ethertype = ntohs(pkt.ether.h_proto);
memcpy(from->mac, pkt.ether.h_source, 6);
memcpy(from->mac_dest, pkt.ether.h_dest, 6);
return ret - sizeof(struct ethhdr);
}
// Network API for WR-PTPd
#ifndef __PTPD_NETIF_H
#define __PTPD_NETIF_H
#include <stdio.h>
//#include <inttypes.h>
#define PTPD_SOCK_RAW_ETHERNET 1
#define PTPD_SOCK_UDP 2
#define PTPD_FLAGS_MULTICAST 0x1
// error codes (to be extended)
#define PTPD_NETIF_READY 1
#define PTPD_NETIF_OK 0
#define PTPD_NETIF_ERROR -1
#define PTPD_NETIF_NOT_READY -2
#define PTPD_NETIF_NOT_FOUND -3
// GCC-specific
#define PACKED __attribute__((packed))
#define PHYS_PORT_ANY (0xffff)
#define PTPD_NETIF_TX 1
#define PTPD_NETIF_RX 2
#define IFACE_NAME_LEN 16
#define SLAVE_PRIORITY_0 0
#define SLAVE_PRIORITY_1 1
#define SLAVE_PRIORITY_2 2
#define SLAVE_PRIORITY_3 3
#define SLAVE_PRIORITY_4 4
// Some system-independent definitions
typedef uint8_t mac_addr_t[6];
typedef uint32_t ipv4_addr_t;
// WhiteRabbit socket - it's void pointer as the real socket structure is private and probably platform-specific.
typedef void *wr_socket_t;
// Socket address for ptp_netif_ functions
typedef struct {
// Network interface name (eth0, ...)
char if_name[IFACE_NAME_LEN];
// Socket family (RAW ethernet/UDP)
int family;
// MAC address
mac_addr_t mac;
// Destination MASC address, filled by recvfrom() function on interfaces bound to multiple addresses
mac_addr_t mac_dest;
// IP address
ipv4_addr_t ip;
// UDP port
uint16_t port;
// RAW ethertype
uint16_t ethertype;
// physical port to bind socket to
uint16_t physical_port;
} wr_sockaddr_t;
typedef struct {
uint32_t v[4];
} wr_picoseconds_t;
// Precise WhiteRabbit timestamp
// TS[picoseconds] = utc * 1e12 + nsec * 1e3 + phase;
PACKED struct _wr_timestamp {
// UTC time value (seconds)
int64_t utc;
// Nanoseconds
int32_t nsec;
// Phase (in picoseconds), linearized for receive timestamps, zero for send timestamps
int32_t phase; // phase(picoseconds)
int32_t raw_phase;
int32_t raw_nsec;
int32_t raw_ahead;
//int cntr_ahead;
};
typedef struct _wr_timestamp wr_timestamp_t;
/* OK. These functions we'll develop along with network card driver. You can write your own UDP-based stubs for testing purposes. */
// Initialization of network interface:
// - opens devices
// - does necessary ioctls()
// - initializes connection with the mighty HAL daemon
int ptpd_netif_init();
// Creates UDP or Ethernet RAW socket (determined by sock_type) bound to bind_addr. If PTPD_FLAG_MULTICAST is set, the socket is
// automatically added to multicast group. User can specify physical_port field to bind the socket to specific switch port only.
wr_socket_t *ptpd_netif_create_socket(int sock_type, int flags, wr_sockaddr_t *bind_addr);
// Sends a UDP/RAW packet (data, data_length) to address provided in wr_sockaddr_t.
// For raw frames, mac/ethertype needs to be provided, for UDP - ip/port.
// Every transmitted frame has assigned a tag value, stored at tag parameter. This value is later used
// for recovering the precise transmit timestamp. If user doesn't need it, tag parameter can be left NULL.
int ptpd_netif_sendto(wr_socket_t *sock, wr_sockaddr_t *to, void *data, size_t data_length, wr_timestamp_t *tx_ts);
// Receives an UDP/RAW packet. Data is written to (data) and length is returned. Maximum buffer length can be specified
// by data_length parameter. Sender information is stored in structure specified in 'from'. All RXed packets are timestamped and the timestamp
// is stored in rx_timestamp (unless it's NULL).
int ptpd_netif_recvfrom(wr_socket_t *sock, wr_sockaddr_t *from, void *data, size_t data_length, wr_timestamp_t *rx_timestamp);
// Closes the socket.
int ptpd_netif_close_socket(wr_socket_t *sock);
int ptpd_netif_poll(wr_socket_t*);
int ptpd_netif_get_hw_addr(wr_socket_t *sock, mac_addr_t *mac);
/*
* Function start HW locking of freq on WR Slave
* return:
* PTPD_NETIF_ERROR - locking not started
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_locking_enable(int txrx, const char *ifaceName, int priority);
/*
*
* return:
*
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_locking_disable(int txrx, const char *ifaceName, int priority);
int ptpd_netif_locking_poll(int txrx, const char *ifaceName, int priority);
/*
* Function turns on calibration (measurement of delay)
* Tx or Rx depending on the txrx param
* return:
* PTPD_NETIF_NOT_READY - if there is calibratin going on on another port
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibrating_enable(int txrx, const char *ifaceName);
/*
* Function turns off calibration (measurement of delay)
* Tx or Rx depending on the txrx param
* return:
* PTPD_NETIF_ERROR - if there is calibratin going on on another port
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibrating_disable(int txrx, const char *ifaceName);
/*
* Function checks if Rx/Tx (depending on the param) calibration is finished
* if finished, returns measured delay in delta
* return:
*
* PTPD_NETIF_OK - locking started
*/
int ptpd_netif_calibrating_poll(int txrx, const char *ifaceName, uint64_t *delta);
/*
* Function turns on calibration pattern.
* return:
* PTPD_NETIF_NOT_READY - if WRSW is busy with calibration on other switch or error occured
* PTPD_NETIF_OK - calibration started
*/
int ptpd_netif_calibration_pattern_enable(const char *ifaceName, unsigned int calibrationPeriod, unsigned int calibrationPattern, unsigned int calibrationPatternLen);
/*
* Function turns off calibration pattern
* return:
* PTPD_NETIF_ERROR - turning off not successful
* PTPD_NETIF_OK - turning off successful
*/
int ptpd_netif_calibration_pattern_disable(const char *ifaceName);
/*
* Function read calibration data if it's available, used at the beginning of PTPWRd to check if
* HW knows already the interface's deltax, and therefore no need for calibration
* return:
* PTPD_NETIF_NOT_FOUND - if deltas are not known
* PTPD_NETIF_OK - if deltas are known, in such case, deltaTx and deltaRx have valid data
*/
int ptpd_netif_read_calibration_data(const char *ifaceName, uint64_t *deltaTx, uint64_t *deltaRx);
#define MACIEK_TMP
#ifdef MACIEK_TMP
int ptpd_netif_select(wr_socket_t*);
int ptpd_netif_get_hw_addr(wr_socket_t *sock, mac_addr_t *mac);
#endif
/*
* Function reads state of the given port (interface in our case), if the port is up, everything is OK, otherwise ERROR
* return:
* PTPD_NETIF_ERROR - if the port is down
* PTPD_NETIF_OK - if the port is up
*/
int ptpd_netif_get_port_state(const char *ifaceName);
/*
* Function looks for a port (interface) for the port number 'number'
* it will return in the argument ifname the port name
* return:
* PTPD_NETIF_ERROR - port not found
* PTPD_NETIF_OK - if the port found
*/
int ptpd_netif_get_ifName(char *ifname, int number);
/* Returns the millisecond "tics" counter value */
uint64_t ptpd_netif_get_msec_tics();
/*
* Function detects external source lock,
*
* return:
* HEXP_EXTSRC_STATUS_LOCKED 0
* HEXP_LOCK_STATUS_BUSY 1
* HEXP_EXTSRC_STATUS_NOSRC 2
*/
int ptpd_netif_extsrc_detection();
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment