Commit ac097472 authored by Miguel Jimenez Lopez's avatar Miguel Jimenez Lopez

tools: Remove DIO and NIC tools.

parent a67dc039
......@@ -24,8 +24,6 @@ LIBOBJ = speclib.o loader-ll.o
LIBSHARED = libspec.so
PROGS = spec-cl spec-fwloader spec-vuart specmem
PROGS += wr-dio-cmd wr-dio-pps wr-dio-agent wr-dio-ruler
PROGS += stamp-frame
all: $(LIB) $(PROGS) $(LIBSHARED)
......
/*
* Copyright (C) 2010,2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include "net_tstamp.h" /* Actually, <linux/net_tstamp.h> */
#ifndef SO_TIMESTAMPNS
# define SO_TIMESTAMPNS 35
# define SCM_TIMESTAMPNS SO_TIMESTAMPNS
#endif
#ifndef SO_TIMESTAMPING
# define SO_TIMESTAMPING 37
# define SCM_TIMESTAMPING SO_TIMESTAMPING
#endif
#ifndef SIOCSHWTSTAMP
# define SIOCSHWTSTAMP 0x89b0
#endif
#ifndef ETH_P_1588
# define ETH_P_1588 0x88F7
#endif
static char git_version[] = "version: " GIT_VERSION;
/* This structure is used to collect stamping information */
struct ts_data {
struct timespec ns;
struct timespec hw[3]; /* software, hw-sys, hw-raw */
int error;
};
/* We can print such stamp info. Returns -1 with errno set on error */
int print_stamp(FILE *out, char *prefix, struct ts_data *tstamp, FILE *err)
{
int i;
static char *names[] = {"sw ", "hw-sys", "hw-raw"};
if (tstamp->error) {
if (err)
fprintf(err, "%s: %s\n", prefix, strerror(errno));
errno = tstamp->error;
return -1;
}
fprintf(out, "%s ns: %10li.%09li\n", prefix, tstamp->ns.tv_sec,
tstamp->ns.tv_nsec);
for (i = 0; i < 3; i++)
fprintf(out, "%s %s: %10li.%09li\n", prefix, names[i],
tstamp->hw[i].tv_sec,
tstamp->hw[i].tv_nsec);
fprintf(out, "\n");
return 0;
}
/*
* This function opens a socket and configures it for stamping.
* It is a library function, in a way, and was used as such
* when part of the "testing" programs of wr-switch-sw
*/
int make_stamping_socket(FILE *errchan, char *argv0, char *ifname,
int tx_type, int rx_filter, int bits,
unsigned char *macaddr)
{
struct ifreq ifr;
struct sockaddr_ll addr;
struct hwtstamp_config hwconfig;
int sock, iindex, enable = 1;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0 && errchan)
fprintf(errchan, "%s: socket(): %s\n", argv0,
strerror(errno));
if (sock < 0)
return sock;
memset (&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, ifname);
/* hw interface information */
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFINDEX(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
iindex = ifr.ifr_ifindex;
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFHWADDR(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
/* Also, enable stamping for the hw interface */
memset(&hwconfig, 0, sizeof(hwconfig));
hwconfig.tx_type = tx_type;
hwconfig.rx_filter = rx_filter;
ifr.ifr_data = (void *)&hwconfig;
if (ioctl(sock, SIOCSHWTSTAMP, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
/* bind and setsockopt */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(ETH_P_ALL);
addr.sll_ifindex = iindex;
addr.sll_pkttype = PACKET_OUTGOING;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
&enable, sizeof(enable)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMP): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
&bits, sizeof(bits)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMPING): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
return sock;
}
/*
* Another "library" function, actually only used by the following two
*/
static void __collect_data(struct msghdr *msgp, struct ts_data *tstamp)
{
struct cmsghdr *cm;
struct timespec *tsptr;
if (!tstamp)
return;
memset(tstamp, 0, sizeof(*tstamp));
/* Extract data from the cmsg */
for (cm = CMSG_FIRSTHDR(msgp); cm; cm = CMSG_NXTHDR(msgp, cm)) {
tsptr = (struct timespec *)CMSG_DATA(cm);
if (0) {
printf("level %i, type %i, len %zi\n", cm->cmsg_level,
cm->cmsg_type, cm->cmsg_len);
}
if (cm->cmsg_level != SOL_SOCKET)
continue;
if (cm->cmsg_type == SO_TIMESTAMPNS)
tstamp->ns = *tsptr;
if (cm->cmsg_type == SO_TIMESTAMPING)
memcpy(tstamp->hw, tsptr, sizeof(tstamp->hw));
}
}
/*
* These functions are like send/recv but handle stamping too.
*/
ssize_t send_and_stamp(int sock, void *buf, size_t len, int flags,
struct ts_data *tstamp)
{
struct msghdr msg; /* this line and more from timestamping.c */
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
char data[3*1024];
int i, j, ret;
ret = send(sock, buf, len, flags);
if (ret < 0)
return ret;
/* Then, get back from the error queue */
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = data;
entry.iov_len = sizeof(data);
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
j = 100; /* number of trials */
while ( (i = recvmsg(sock, &msg, MSG_ERRQUEUE)) < 0 && j--)
usleep(10000); /* retry for 1 second */
if (i < 0) {
if (tstamp) {
memset(tstamp, 0, sizeof(*tstamp));
tstamp->error = ETIMEDOUT;
}
return ret;
}
if (getenv("STAMP_VERBOSE")) {
int b;
printf("send %i =", i);
for (b = 0; b < i && b < 20; b++)
printf(" %02x", data[b] & 0xff);
putchar('\n');
}
/* FIX<E: Check that the actual data is what we sent */
__collect_data(&msg, tstamp);
return ret;
}
ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags,
struct ts_data *tstamp)
{
int ret;
struct msghdr msg;
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
if (0) {
/* we can't really call recv, do it with cmsg alone */
ret = recv(sock, buf, len, flags);
} else {
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = buf;
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);
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
return ret;
if (getenv("STAMP_VERBOSE")) {
int b;
printf("recv %i =", ret);
for (b = 0; b < ret && b < 20; b++)
printf(" %02x", ((char *)buf)[b] & 0xff);
putchar('\n');
}
__collect_data(&msg, tstamp);
}
return ret;
}
/* Add and subtract timespec */
void ts_add(struct timespec *t1, struct timespec *t2)
{
t1->tv_sec += t2->tv_sec;
t1->tv_nsec += t2->tv_nsec;
if (t1->tv_nsec > 1000 * 1000 * 1000) {
t1->tv_nsec -= 1000 * 1000 * 1000;
t1->tv_sec++;
}
}
void ts_sub(struct timespec *t1, struct timespec *t2)
{
t1->tv_sec -= t2->tv_sec;
t1->tv_nsec -= t2->tv_nsec;
if (t1->tv_nsec < 0) {
t1->tv_nsec += 1000 * 1000 * 1000;
t1->tv_sec--;
}
}
/*
* Ok, the library-like part is over, we are at main now
*/
#define STAMP_PROTO 0xf001
/* This is the frame we are exchanging back and forth */
struct frame {
struct ether_header h;
uint16_t phase; /* 0 = transmit, 1 = tx follow up */
struct timespec ts[4];
unsigned char filler[64]; /* to reach minimum size for sure */
};
void report_times(struct timespec *ts)
{
struct timespec rtt, tmp, fw, bw;
int i;
for (i = 0; i < 4; i++)
printf("timestamp T%i: %9li.%09li\n", i+1,
ts[i].tv_sec, ts[i].tv_nsec);
/* calculate round trip time, forward, backward */
rtt = ts[3];
ts_sub(&rtt, &ts[0]);
tmp = ts[2];
ts_sub(&tmp, &ts[1]);
ts_sub(&rtt, &tmp);
fw = ts[1];
ts_sub(&fw, &ts[0]);
bw = ts[3];
ts_sub(&bw, &ts[2]);
printf("round trip time: %9li.%09li\n", rtt.tv_sec, rtt.tv_nsec);
printf("forward time: %9li.%09li\n", fw.tv_sec, fw.tv_nsec);
printf("backward time: %9li.%09li\n", bw.tv_sec, bw.tv_nsec);
}
/* send a frame, and then again after filling the tx time at offset given */
void send_one_with_followup(int sock, struct frame *f, unsigned char *mac,
int offset)
{
struct ts_data stamp;
/* Fill ether header */
memset(&f->h.ether_dhost, 0xff, ETH_ALEN); /* broadcast */
memcpy(&f->h.ether_shost, mac, ETH_ALEN);
f->h.ether_type = htons(STAMP_PROTO);
f->phase = 0;
if (send_and_stamp(sock, f, sizeof(*f), 0, &stamp) < 0)
fprintf(stderr, "send_and_stamp: %s\n", strerror(errno));
f->phase = 1;
f->ts[offset] = stamp.hw[2]; /* hw raw */
if (send_and_stamp(sock, f, sizeof(*f), 0, NULL) < 0)
fprintf(stderr, "send_and_stamp: %s\n", strerror(errno));
if (getenv("STAMP_PRINT_ALL"))
print_stamp(stdout, "send", &stamp, stderr);
}
/* receive a frame, timestamping it, and receive the followup too; save ts */
void recv_one_with_followup(int sock, struct frame *f, unsigned char *mac,
int offset)
{
struct ts_data stamp;
int ret;
while (1) { /* repeat until a good frame is received */
ret = recv_and_stamp(sock, f, sizeof(*f), MSG_TRUNC, &stamp);
if (ret < 0) {
fprintf(stderr, "recv_and_stamp: %s\n",
strerror(errno));
continue;
}
if (ret != sizeof(*f))
continue;
if (ntohs(f->h.ether_type) != STAMP_PROTO)
continue;
if (f->phase != 0)
continue;
break;
}
/* receive another one, lazily don't wait */
if (recv_and_stamp(sock, f, sizeof(*f), MSG_TRUNC, NULL) < 0)
fprintf(stderr, "recv_and_stamp: %s\n",
strerror(errno));
f->ts[offset] = stamp.hw[2];
if (getenv("STAMP_PRINT_ALL"))
print_stamp(stdout, "recv", &stamp, stderr);
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
static struct frame f;
int sock;
unsigned char macaddr[6];
int listenmode = 0;
int howto = SOF_TIMESTAMPING_MASK; /* everything */
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
/* From ./net_tstamp.h, these are the "howto" values
*
* SOF_TIMESTAMPING_TX_HARDWARE = 1,
* SOF_TIMESTAMPING_TX_SOFTWARE = 2
* SOF_TIMESTAMPING_RX_HARDWARE = 4,
* SOF_TIMESTAMPING_RX_SOFTWARE = 8,
* SOF_TIMESTAMPING_SOFTWARE = 16,
* SOF_TIMESTAMPING_SYS_HARDWARE = 32,
* SOF_TIMESTAMPING_RAW_HARDWARE = 64,
*/
if (argc == 3 && !strcmp(argv[2], "listen")) {
listenmode = 1;
argc--;
}
if (argc != 2) {
fprintf(stderr, "%s: Use \"%s [-V] <ifname> [listen]\n", argv[0],
argv[0]);
exit(1);
}
printf("%s: Using interface %s, with all timestamp options active\n",
argv[0], argv[1]);
/* Create a socket to use for stamping, use the library code above */
sock = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr);
if (sock < 0) /* message already printed */
exit(1);
if (listenmode) {
/* forever reply */
while (1) {
recv_one_with_followup(sock, &f, macaddr, 1);
send_one_with_followup(sock, &f, macaddr, 2);
}
}
/* send mode: send first, then receive, then print */
send_one_with_followup(sock, &f, macaddr, 0);
recv_one_with_followup(sock, &f, macaddr, 3);
report_times(f.ts);
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* Typical use: "wr-dio-agent wr1" */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "wr_nic/wr-nic.h"
#include "wr-dio.h"
static char git_version[] = "version: " GIT_VERSION;
#define RULER_PROTO 0x5752 /* WR */
/*
* Lazily, use global variables, so the code has less parameter passing.
* Everything in this file is using "agent_" as a prefix, to ease the
* reader -- and remember that <TAB> aligns at 8 spaces, not 4.
*/
char *agent_prgname;
int agent_sock;
char *agent_ifname;
struct ifreq agent_ifr;
/* Boring network stuff extracted from main function */
static int agent_open_wr_sock(char *name)
{
struct sockaddr_ll addr;
int sock, ifindex;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
agent_prgname, strerror(errno));
return -1;
}
memset(&agent_ifr, 0, sizeof(agent_ifr));
strncpy(agent_ifr.ifr_name, name, sizeof(agent_ifr.ifr_name));
if (ioctl(sock, PRIV_MEZZANINE_ID, &agent_ifr) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
agent_prgname, name, strerror(errno));
close(sock);
return -1;
}
/* Retieve the interfaceindex */
if (ioctl(sock, SIOCGIFINDEX, &agent_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFINDEX(%s): %s\n", agent_prgname,
name, strerror(errno));
close(sock);
return -1;
}
ifindex = agent_ifr.ifr_ifindex;
/* Bind to the interface, so to be able to receive */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(RULER_PROTO);
addr.sll_ifindex = ifindex;
addr.sll_pkttype = PACKET_BROADCAST; /* that's what ruler sends */
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(%s): %s\n", agent_prgname,
name, strerror(errno));
close(sock);
return -1;
}
/* save in globals for later */
agent_sock = sock;
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
/* And a simple main with the loop inside */
int main(int argc, char **argv)
{
int len;
/* We are receiving stuff in this frame */
static struct frame {
struct ether_header h;
unsigned char pad[2];
struct wr_dio_cmd cmd;
} f;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc != 2) {
fprintf(stderr, "%s: Use \"%s [-V] <wr-if>\"\n",
argv[0], argv[0]);
exit(1);
}
agent_prgname = argv[0];
/* All functions print error messages by themselves, so just exit */
if (agent_open_wr_sock(argv[1]) < 0)
exit(1);
while (1) {
len = recv(agent_sock, &f, sizeof(f), MSG_TRUNC);
if (len != sizeof(f)) {
fprintf(stderr, "%s: recevied unexpected frame length"
" (%i instead of %zu)\n", agent_prgname, len,
sizeof(f));
continue;
}
if (ntohs(f.h.ether_type) != RULER_PROTO) {
fprintf(stderr, "%s: received unexpected eth type"
" (%04x instead of %04x)\n", agent_prgname,
ntohs(f.h.ether_type), RULER_PROTO);
continue;
}
if (0)
printf("command %i, ch %i, t %li.%09li\n",
f.cmd.command, f.cmd.channel, f.cmd.t[0].tv_sec,
f.cmd.t[0].tv_nsec);
/* Then simply pass it to the hardware */
agent_ifr.ifr_data = (void *)&f.cmd;
if (ioctl(agent_sock, PRIV_MEZZANINE_CMD, &agent_ifr) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", agent_prgname, agent_ifname,
strerror(errno));
return -1;
}
}
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "wr_nic/wr-nic.h"
#include "wr-dio.h"
static char git_version[] = "version: " GIT_VERSION;
char *prgname;
char c;
int sock;
char *ifname;
struct ifreq ifr;
struct wr_dio_cmd _cmd;
struct wr_dio_cmd *cmd = &_cmd;
static int parse_ts(char *s, struct timespec *ts)
{
int i, n;
unsigned long nano;
char c;
/*
* Hairy: if we scan "%ld%lf", the 0.009999 will become 9998 micro.
* Thus, scan as integer and string, so we can count leading zeros
*/
nano = 0;
ts->tv_sec = 0;
ts->tv_nsec = 0;
if ( (i = sscanf(s, "%ld.%ld%c", &ts->tv_sec, &nano, &c)) == 1)
return 0; /* seconds only */
if (i == 3)
return -1; /* trailing crap */
if (i == 0)
if (sscanf(s, ".%ld%c", &nano, &c) != 1)
return -1; /* leading or trailing crap */
s = strchr(s, '.') + 1;
n = strlen(s);
if (n > 9)
return -1; /* too many decimals */
while (n < 9) {
nano *= 10;
n++;
}
ts->tv_nsec = nano;
return 0;
}
static int scan_pulse(int argc, char **argv)
{
char c;
if (argc != 4 && argc != 6) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
fprintf(stderr, " Use: %s <channel> <duration> <when> "
"[<period> <count>]\n", argv[0]);
return -1;
}
if (sscanf(argv[1], "%hi%c", &cmd->channel, &c) != 1
|| cmd->channel > 4) {
fprintf(stderr, "%s: %s: not a channel number \"%s\"\n",
prgname, argv[0], argv[1]);
return -1;
}
/* Duration is first time argument but position 1 for ioctl */
if (parse_ts(argv[2], cmd->t + 1) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[2]);
return -1;
}
if (cmd->t[1].tv_sec) {
fprintf(stderr, "%s: %s: duration must be < 1s (got \"%s\")\n",
prgname, argv[0], argv[2]);
return -1;
}
/* Next argument is the "when", position 0 in ioctl timestamp array */
if (!strcmp(argv[3], "now")) {
cmd->flags |= WR_DIO_F_NOW;
} else {
char *s2 = argv[3];
if (s2[0] == '+') {
cmd->flags |= WR_DIO_F_REL;
s2++;
}
if (parse_ts(s2, cmd->t) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[3]);
return -1;
}
}
/* If argc is 6, we have period and count */
if (argc == 6) {
cmd->flags |= WR_DIO_F_LOOP;
if (parse_ts(argv[4], cmd->t + 2) < 0) {
fprintf(stderr, "%s: %s: invalid time \"%s\"\n",
prgname, argv[0], argv[4]);
return -1;
}
if (sscanf(argv[5], "%i%c", &cmd->value, &c) != 1) {
fprintf(stderr, "%s: %s: invalid count \"%s\"\n",
prgname, argv[0], argv[5]);
return -1;
}
}
ifr.ifr_data = (void *)cmd;
if (ioctl(sock, PRIV_MEZZANINE_CMD, &ifr) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, ifname, strerror(errno));
return -1;
}
return 0;
}
static int scan_stamp(int argc, char **argv, int ismask)
{
int i, ch;
char c;
cmd->flags = 0;
if (argc == 3 && !strcmp(argv[2], "wait")) {
cmd->flags = WR_DIO_F_WAIT;
argc = 2;
}
if (argc == 1) {
ismask = 1;
ch = 0x1f;
} else if (argc == 2) {
if (sscanf(argv[1], "%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: %s: not a channel \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
if (ch < 0 || ch > 31 || (!ismask && ch > 4)) {
fprintf(stderr, "%s: %s: out of range channel \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
} else {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
if (ismask)
fprintf(stderr, " Use: %s [<channel-mask>]\n",
argv[0]);
else
fprintf(stderr, " Use: %s [<channel>] [wait]\n",
argv[0]);
return -1;
}
if (ismask)
cmd->flags = WR_DIO_F_MASK;
while (1) {
cmd->channel = ch;
errno = 0;
ifr.ifr_data = (void *)cmd;
if (ioctl(sock, PRIV_MEZZANINE_CMD, &ifr) < 0 ) {
if (errno == EAGAIN)
break;
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", prgname, ifname, strerror(errno));
return -1;
}
for (i = 0; i < cmd->nstamp; i++)
printf("ch %i, %9li.%09li\n", cmd->channel,
(long)cmd->t[i].tv_sec, cmd->t[i].tv_nsec);
}
return 0;
}
static int one_mode(int c, int index)
{
if (c == '-')
return 0;
cmd->channel |= 1 << index;
//Add error message for channel 0
if(index==0 && strchr("dD01",c))
{
fprintf(stderr, "Error: Only p/P modes are available as ouput mode for channel 0\n");
return -1;
}
switch(c) {
case 'D':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'd':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
case 'C':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'c':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_VALUE << index;
if(index!=4)
fprintf(stdout, "Warning: Clock mode is only available for last channel (ch4)\n,"
"(on other channel it corresponds to input mode without interruptions)\n");
break;
case 'P':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'p':
cmd->value |= WR_DIO_INOUT_DIO << index;
cmd->value |= WR_DIO_INOUT_VALUE << index;
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
case 'I':
cmd->value |= WR_DIO_INOUT_TERM << index;
case 'i':
break;
case '1':
cmd->value |= WR_DIO_INOUT_VALUE << index;
case '0':
cmd->value |= WR_DIO_INOUT_OUTPUT << index;
break;
default:
fprintf(stderr, "%s: mode: invalid mode '%c'\n",
prgname, c);
return -1;
}
return 0;
}
static int scan_inout(int argc, char **argv)
{
int i, ch;
char c;
cmd->flags = WR_DIO_F_MASK;
cmd->channel = 0;
cmd->value = 0;
if (argc == 2) {
if (strlen(argv[1]) != 5) {
fprintf(stderr, "%s: %s: wrong argument \"%s\"\n",
prgname, argv[0], argv[1]);
exit(1);
}
for (i = 0; i < 5; i++)
if (one_mode(argv[1][i], i) < 0)
return -1;
} else {
if (argc < 3 || argc > 11 || ((argc & 1) == 0)) {
fprintf(stderr, "%s: %s: wrong number of arguments\n",
prgname, argv[0]);
return -1;
}
while (argc >= 3) {
if (sscanf(argv[1], "%i%c", &ch, &c) != 1
|| ch < 0 || ch > 4) {
fprintf(stderr, "%s: mode: invalid channel "
"\"%s\"\n", prgname, argv[1]);
return -1;
}
if (strlen(argv[2]) != 1) {
fprintf(stderr, "%s: mode: invalid mode "
"\"%s\"\n", prgname, argv[2]);
return -1;
}
if (one_mode(argv[2][0], ch) < 0)
return -1;
argv += 2;
argc -= 2;
}
}
ifr.ifr_data = (void *)cmd;
if (ioctl(sock, PRIV_MEZZANINE_CMD, &ifr) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, ifname, strerror(errno));
return -1;
}
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
prgname = argv[0];
argv++, argc--;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 2) {
fprintf(stderr, "%s: use \"%s [-V] <netdev> <cmd> [...]\"\n",
prgname, prgname);
exit(1);
}
ifname = argv[0];
argv++, argc--;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
prgname, strerror(errno));
exit(1);
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, PRIV_MEZZANINE_ID, &ifr) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
prgname, ifname, strerror(errno));
}
/*
* Parse the command line:
*
* pulse <ch> .<len> <seconds>.<fraction>
* pulse <ch> .<len> now
* pulse <ch> .<len> +<seconds>.<fraction>
*
* stamp [<channel>]
* stampm [<mask>]
*
* mode <01234>
* mode <ch> <mode> [...]
*/
if (!strcmp(argv[0], "pulse")) {
cmd->command = WR_DIO_CMD_PULSE;
if (scan_pulse(argc, argv) < 0)
exit(1);
} else if (!strcmp(argv[0], "stamp")) {
cmd->command = WR_DIO_CMD_STAMP;
if (scan_stamp(argc, argv, 0 /* no mask */) < 0)
exit(1);
} else if (!strcmp(argv[0], "stampm")) {
cmd->command = WR_DIO_CMD_STAMP;
if (scan_stamp(argc, argv, 1 /* mask */) < 0)
exit(1);
} else if (!strcmp(argv[0], "mode")) {
cmd->command = WR_DIO_CMD_INOUT;
if (scan_inout(argc, argv) < 0)
exit(1);
} else {
fprintf(stderr, "%s: unknown command \"%s\"\n", prgname,
argv[0]);
exit(1);
}
ifr.ifr_data = (void *)cmd;
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "wr_nic/wr-nic.h"
#include "wr-dio.h"
/**
* This takes two arguments: interface name and channel number
*
* This simple tools just show an example of how to program with wr-nic/dio.
* If you want to measure WR timing we suggest to use the hardwire PPS on channel 0
* that gives a more accurate precision.
**/
static char git_version[] = "version: " GIT_VERSION;
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
int main(int argc, char **argv)
{
struct wr_dio_cmd _cmd;
struct wr_dio_cmd *cmd = &_cmd;
struct ifreq ifr;
char *prgname = argv[0];
char *ifname = "wr0";
int sock, ch, charg = 1;
char c;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 2 || argc > 3) {
fprintf(stderr, "%s: Use \"%s [<ifname>] <channel>\"\n",
prgname, prgname);
exit(1);
}
if (argc == 3) {
ifname = argv[1];
charg++;
}
if (sscanf(argv[charg], "%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: Not a channel number \"%s\"\n",
prgname, argv[charg]);
exit(1);
}
if (ch < 1 || ch > 4) {
fprintf(stderr, "%s: Out of range channel number \"%s\"\n",
prgname, argv[charg]);
exit(1);
}
fprintf(stderr, "%s: Using interface \"%s\" and channel %i for "
"pps output\n", prgname, ifname, ch);
/* The following is standard stuff to access wr-nic */
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
prgname, strerror(errno));
exit(1);
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, PRIV_MEZZANINE_ID, &ifr) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
prgname, ifname, strerror(errno));
}
/* Fill the command structure */
memset(cmd, 0, sizeof(*cmd));
cmd->command = WR_DIO_CMD_PULSE;
cmd->channel = ch;
cmd->flags = WR_DIO_F_REL | WR_DIO_F_LOOP;
/* Number of loops: -1 <=> Inf */
cmd->value = -1;
/* 2s delay to have time to send and process this command */
cmd->t[0].tv_sec = 2;
/* 1ms pulse width */
cmd->t[1].tv_nsec = 1000 * 1000;
/* Loop period */
cmd->t[2].tv_sec = 1;
/* Give it to the driver and we are done */
ifr.ifr_data = (void *)cmd;
if (ioctl(sock, PRIV_MEZZANINE_CMD, &ifr) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): %s\n",
prgname, ifname, strerror(errno));
exit(1);
}
exit(0);
}
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released to the public domain as sample code to be customized.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* Typical use: "wr-dio-ruler wr1 IN0 L3+0.001 R3+0.001 L4+0.002" */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include "wr_nic/wr-nic.h"
#include "wr-dio.h"
static char git_version[] = "version: " GIT_VERSION;
#define RULER_PROTO 0x5752 /* WR */
/*
* Lazily, use global variables, so the code has less parameter passing.
* Everything in this file is using "ruler_" as a prefix, to ease the
* reader -- and remember that <TAB> aligns at 8 spaces, not 4.
*/
char *ruler_prgname;
int ruler_sock;
char *ruler_ifname;
struct wr_dio_cmd ruler_cmd;
struct ifreq ruler_ifr;
unsigned char ruler_macaddr[ETH_ALEN];
struct ruler_action {
int isremote;
int channel;
struct timespec delay;
};
/* Boring parsing separated to a separate function (same code as elsewhere) */
static int parse_ts(char *s, struct timespec *ts)
{
int i, n;
unsigned long nano;
char c;
/*
* Hairy: if we scan "%ld%lf", the 0.009999 will become 9998 micro.
* Thus, scan as integer and string, so we can count leading zeros
*/
nano = 0;
ts->tv_sec = 0;
ts->tv_nsec = 0;
if ( (i = sscanf(s, "%ld.%ld%c", &ts->tv_sec, &nano, &c)) == 1)
return 0; /* seconds only */
if (i == 3)
return -1; /* trailing crap */
if (i == 0)
if (sscanf(s, ".%ld%c", &nano, &c) != 1)
return -1; /* leading or trailing crap */
s = strchr(s, '.') + 1;
n = strlen(s);
if (n > 9)
return -1; /* too many decimals */
while (n < 9) {
nano *= 10;
n++;
}
ts->tv_nsec = nano;
return 0;
}
/* Boring network stuff extracted from main function */
static int ruler_open_wr_sock(char *name)
{
struct sockaddr_ll addr;
int sock, ifindex;
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n",
ruler_prgname, strerror(errno));
return -1;
}
memset(&ruler_ifr, 0, sizeof(ruler_ifr));
strncpy(ruler_ifr.ifr_name, name, sizeof(ruler_ifr.ifr_name));
if (ioctl(sock, PRIV_MEZZANINE_ID, &ruler_ifr) < 0
/* EAGAIN is special: it means we have no ID to check yet */
&& errno != EAGAIN) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_ID(%s)): %s\n",
ruler_prgname, name, strerror(errno));
close(sock);
return -1;
}
/* Retrieve the local MAC address to send correct Ethernet frames */
if (ioctl(sock, SIOCGIFHWADDR, &ruler_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFHWADDR(%s): %s\n", ruler_prgname,
name, strerror(errno));
close(sock);
return -1;
}
memcpy(ruler_macaddr, ruler_ifr.ifr_hwaddr.sa_data,
sizeof(ruler_macaddr));
/* Retieve the interfaceindex */
if (ioctl(sock, SIOCGIFINDEX, &ruler_ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFINDEX(%s): %s\n", ruler_prgname,
name, strerror(errno));
close(sock);
return -1;
}
ifindex = ruler_ifr.ifr_ifindex;
/* Bind to the interface, so to be able to send */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(RULER_PROTO);
addr.sll_ifindex = ifindex;
addr.sll_pkttype = PACKET_OUTGOING;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(%s): %s\n", ruler_prgname,
name, strerror(errno));
close(sock);
return -1;
}
/* save in globals for later */
ruler_sock = sock;
ruler_ifname = name;
return 0;
}
/* The channel being monitored will be configured as input */
static int ruler_config_in(char *chname)
{
int ch;
char c;
if (sscanf(chname, "IN%i%c", &ch, &c) != 1) {
fprintf(stderr, "%s: Argument \"%s\" must be \"IN<ch>\"\n",
ruler_prgname, chname);
return -1;
}
if (ch < 0 || ch > 4) {
fprintf(stderr, "%s: Out of range channel number in \"%s\"\n",
ruler_prgname, chname);
return -1;
}
return ch;
}
/* Actions are allocated in an array and returned for later use */
static struct ruler_action *ruler_build_actions(int nact, char **strings)
{
struct ruler_action *act;
char *s;
int i, ch;
char lr, c;
act = calloc(nact, sizeof(*act));
if (!act) {
fprintf(stderr, "%s: %s\n", ruler_prgname, strerror(errno));
return NULL;
}
for (i = 0; i < nact; i++) {
if (sscanf(strings[i], "%c%i+%c\n", &lr, &ch, &c) != 3) {
fprintf(stderr, "%s: Wrong argument \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
if (lr != 'L' && lr != 'R') {
fprintf(stderr, "%s: Wrong argument \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
if (ch < 0 || ch > 4) {
fprintf(stderr, "%s: Out of range channel in \"%s\"\n",
ruler_prgname, strings[i]);
free(act);
return NULL;
}
s = strchr(strings[i], '+') + 1;
if (parse_ts(s, &act[i].delay) < 0) {
fprintf(stderr, "%s: Wrong time \"%s\" in \"%s\"\n",
ruler_prgname, s, strings[i]);
free(act);
return NULL;
}
if (lr == 'L')
act[i].isremote = 0;
else
act[i].isremote = 1;
act[i].channel = ch;
}
for (i = 0; i < nact; i++) {
fprintf(stderr, "%s: configured for %s channel %i, "
"delay %li.%09li\n", ruler_prgname,
act[i].isremote ? "remote" : " local",
act[i].channel,
act[i].delay.tv_sec, act[i].delay.tv_nsec);
}
return act;
}
/* The main loop will wait for an event... */
static int ruler_wait_event(int inch, struct timespec *ts)
{
ruler_cmd.command = WR_DIO_CMD_STAMP;
ruler_cmd.flags = WR_DIO_F_WAIT;
ruler_cmd.channel = inch;
ruler_ifr.ifr_data = (void *)&ruler_cmd;
if (ioctl(ruler_sock, PRIV_MEZZANINE_CMD, &ruler_ifr) < 0 ) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", ruler_prgname, ruler_ifname, strerror(errno));
return -1;
}
/* Assume it's only one stamp */
*ts = ruler_cmd.t[0];
return 0;
}
/* ...and run all actions when the event happens */
static int ruler_run_actions(int nact, struct timespec *ts,
struct ruler_action *actions)
{
int i;
/* We are building stuff in this frame, to possibly send it */
static struct frame {
struct ether_header h;
unsigned char pad[2];
struct wr_dio_cmd cmd;
} f;
/* Most parameters are unchanged over actions */
memset(&f.h.ether_dhost, 0xff, ETH_ALEN); /* broadcast */
memcpy(&f.h.ether_shost, ruler_macaddr, ETH_ALEN);
f.h.ether_type = ntohs(RULER_PROTO);
f.cmd.t[1].tv_nsec = 1000 * 1000; /* 1ms*/
f.cmd.command = WR_DIO_CMD_PULSE;
for (i = 0; i < nact; i++) {
f.cmd.channel = actions[i].channel;
f.cmd.t[0] = *ts;
/* add the requested delay */
f.cmd.t[0].tv_sec += actions[i].delay.tv_sec;
f.cmd.t[0].tv_nsec += actions[i].delay.tv_nsec;
if (f.cmd.t[0].tv_nsec > 1000 * 1000 * 1000) {
f.cmd.t[0].tv_nsec -= 1000 * 1000 * 1000;
f.cmd.t[0].tv_sec++;
}
if (actions[i].isremote) {
if (send(ruler_sock, &f, sizeof(f), 0) < 0) {
fprintf(stderr, "%s: send(): %s\n",
ruler_prgname, strerror(errno));
return -1;
}
continue;
}
/* local */
ruler_ifr.ifr_data = (void *)&f.cmd;
if (ioctl(ruler_sock, PRIV_MEZZANINE_CMD, &ruler_ifr) < 0) {
fprintf(stderr, "%s: ioctl(PRIV_MEZZANINE_CMD(%s)): "
"%s\n", ruler_prgname, ruler_ifname,
strerror(errno));
return -1;
}
}
return 0;
}
static void print_version(char *pname)
{
printf("%s %s\n", pname, git_version);
}
/* Finally, a main function to wrap it all */
int main(int argc, char **argv)
{
struct ruler_action *actions;
struct timespec ts;
int inch;
if ((argc == 2) && (!strcmp(argv[1], "-V"))) {
print_version(argv[0]);
exit(0);
}
if (argc < 4) {
fprintf(stderr, "%s: Use \"%s [-V] <wr-if> IN<ch> "
"{L,R}<ch>+<delay-as-decimal> [...]\n",
argv[0], argv[0]);
exit(1);
}
ruler_prgname = argv[0];
/* All functions print error messages by themselves, so just exit */
if (ruler_open_wr_sock(argv[1]) < 0)
exit(1);
inch = ruler_config_in(argv[2]);
if (inch < 0)
exit(1);
actions = ruler_build_actions(argc - 3, argv + 3);
if (!actions)
exit(1);
while(1) {
if (ruler_wait_event(inch, &ts) < 0)
exit(1);
if (ruler_run_actions(argc - 3, &ts, actions) < 0)
exit(1);
}
exit(0);
}
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