Commit 7fe26514 authored by Alessandro Rubini's avatar Alessandro Rubini

test: misc/: two trivial source files with a simple library

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent f3b60f07
CFLAGS = -Wall -O2 -ggdb
ALL = ttstamp onestamp
all: $(ALL)
ttstamp: ttstamp.o stamp-funcs.o
onestamp: onestamp.o stamp-funcs.o
clean:
rm -f $(ALL) *.o *~
\ No newline at end of file
/* Common stuff for these misc tools */
#include <sys/types.h>
#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
extern int make_stamping_socket(FILE *errchan, char *argv0, char *ifname,
int tx_type, int rx_filter, int bits,
unsigned char *macaddr);
extern ssize_t send_and_stamp(int sock, void *buf, size_t len, int flags);
extern ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags);
extern int print_stamp(FILE *out, char *prefix, FILE *err /* may be NULL */);
extern int get_stamp(struct timespec ts[4]);
/* Functions to make the difference between timevals and timespecs */
static inline int tvdiff(struct timeval *tv2, struct timeval *tv1)
{
return (tv2->tv_sec - tv1->tv_sec) * 1000 * 1000
+ tv2->tv_usec - tv1->tv_usec;
}
static inline int tsdiff(struct timespec *tv2, struct timespec *tv1)
{
return (tv2->tv_sec - tv1->tv_sec) * 1000 * 1000 * 1000
+ tv2->tv_nsec - tv1->tv_nsec;
}
/*
* A simple tool to timestamp one tx and one rx packet
* Alessandro Rubini 2011, GPL2 or later
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "net_tstamp.h"
#include "misc-common.h"
#define BSIZE 2048
char pkt[BSIZE];
int main(int argc, char **argv)
{
int sock;
unsigned char macaddr[6];
char *rest;
int howto;
if (argc != 3) {
fprintf(stderr, "%s: Use \"%s <ifname> <bitmask>\n", argv[0],
argv[0]);
exit(1);
}
howto = strtol(argv[2], &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: \"%s\" is not a number\n", argv[0],
argv[2]);
exit(1);
}
/* 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,
*/
printf("%s: Using interface %s, with %i as SO_TIMESTAMPING\n",
argv[0], argv[1], howto);
/* Create a socket to use for stamping, use the library code */
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);
/* build a packet */
memcpy(pkt + 0, "\xff\xff\xff\xff\xff\xff", 6);
memcpy(pkt + 6, macaddr, 6);
memcpy(pkt + 12, "xx", 2);
if (send_and_stamp(sock, pkt, 64, 0) < 0) {
fprintf(stderr, "%s: send_and_stamp: %s\n", argv[0],
strerror(errno));
} else {
print_stamp(stdout, "tx", stderr);
}
if (recv_and_stamp(sock, pkt, sizeof(pkt), 0) < 0) {
fprintf(stderr, "%s: recv_and_stamp: %s\n", argv[0],
strerror(errno));
} else {
print_stamp(stdout, "rx", stderr);
}
exit(0);
}
/*
* This file is a "library" file which offers functions with the same
* Interface as send() and recv() but that handle stamping as well.
* The timestamp informazion is saved to global variables, so no
* concurrency is allowed and so on. In short: it's as crappy as
* possible, but not crappier.
*
* Alessandro Rubini 2011, GPL2 or later
*/
#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"
#include "misc-common.h"
/* This functions opens a socket and configures it for stamping */
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 */
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(ETH_P_ALL);
addr.sll_ifindex = iindex;
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_TIMESTAMPNS,
&enable, sizeof(enable)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMPNS): %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;
}
/* This static data is used to collect stamping information */
static struct timespec __stamp_ns;
static struct timespec __stamp_hw_info[3];
static int __stamp_errno;
/* We can print such stamp info */
int print_stamp(FILE *out, char *prefix, FILE *err)
{
int i;
if (__stamp_errno) {
if (err)
fprintf(err, "get_timestamp: %s\n", strerror(errno));
errno = __stamp_errno;
return -1;
}
fprintf(out, "%s ns: %10li.%09li\n", prefix, __stamp_ns.tv_sec,
__stamp_ns.tv_nsec);
for (i = 0; i < 3; i++)
fprintf(out, "%s ns%i: %10li.%09li\n", prefix, i,
__stamp_hw_info[i].tv_sec,
__stamp_hw_info[i].tv_nsec);
fprintf(out, "\n");
return 0;
}
/* Or we can return it to the caller -- FIXME */
int get_stamp(struct timespec ts[4])
{
if (__stamp_errno) {
errno = __stamp_errno;
memset(ts, 0, 4 * sizeof(ts[0]));
return -1;
}
ts[0] = __stamp_ns;
memcpy(ts + 1, __stamp_hw_info, sizeof(__stamp_hw_info));
return 0;
}
/* This collecting of data is in common between send and recv */
static void __collect_data(struct msghdr *msgp)
{
struct cmsghdr *cmsg;
int i;
__stamp_errno = 0;
memset(&__stamp_ns, 0, sizeof(__stamp_ns));
memset(__stamp_hw_info, 0, sizeof(__stamp_hw_info));
/* Ok, got the tx stamp. Now collect stamp data */
for (cmsg = CMSG_FIRSTHDR(msgp); cmsg;
cmsg = CMSG_NXTHDR(msgp, cmsg)) {
struct timespec *stamp_ptr;
if (0) {
printf("level %i, type %i, len %i\n", cmsg->cmsg_level,
cmsg->cmsg_type, cmsg->cmsg_len);
}
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
stamp_ptr = (struct timespec *)CMSG_DATA(cmsg);
if (cmsg->cmsg_type == SO_TIMESTAMPNS)
__stamp_ns = *stamp_ptr;
if (cmsg->cmsg_type != SO_TIMESTAMPING)
continue;
for (i = 0; i < 3; i++, stamp_ptr++)
__stamp_hw_info[i] = *stamp_ptr;
}
}
/*
* 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 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 = 10; /* number of trials */
while ( (i = recvmsg(sock, &msg, MSG_ERRQUEUE)) < 0 && j--)
usleep(1000); /* retry... */
if (i < 0) {
__stamp_errno = ETIMEDOUT;
memset(&__stamp_ns, 0, sizeof(__stamp_ns));
memset(__stamp_hw_info, 0, sizeof(__stamp_hw_info));
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');
}
__collect_data(&msg);
return ret;
}
ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags)
{
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);
}
return ret;
}
/*
* A simple tool to test packet transmission, with or without hw stamping
* Alessandro Rubini 2011, GPL2 or later
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.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"
#include "misc-common.h"
#define BSIZE 2048 /* Packet buffer */
static int howto = 1;
static char pkt[2][BSIZE];
int main(int argc, char **argv)
{
int sock[2];
unsigned char macaddr[2][6];
int i, j, k, nloop=1000;
if (argc != 3) {
fprintf(stderr, "%s: Use: \"%s <ethname> <ethname>\"\n",
argv[0], argv[0]);
exit(1);
}
/* Extra arguments from environment */
if (getenv("STAMP_NLOOP"))
nloop = atoi(getenv("STAMP_NLOOP"));
if (getenv("STAMP_HOWTO"))
howto = atoi(getenv("STAMP_HOWTO"));
fprintf(stderr, "%s: working on %s and %s, %i loops, %02x stamp\n",
argv[0], argv[1], argv[2], nloop, howto);
/* Create two sockets */
sock[0] = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr[0]);
if (sock[0] < 0)
exit(1);
sock[1] = make_stamping_socket(stderr, argv[0], argv[2],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr[1]);
if (sock[1] < 0)
exit(1);
/* Build the packet */
memcpy(pkt[0]+0, macaddr[1], 6);
memcpy(pkt[0]+6, macaddr[0], 6);
pkt[0][12] = ETH_P_1588 >> 8;
pkt[0][13] = ETH_P_1588 & 0xff;
for (i = 64; i < 1500; i += 64) {
/* there are 4 timespecs to consider */
unsigned long min[4], avg[4], max[4];
unsigned long long tot[4];
for (k = 0; k < 4; k++) {
max[k] = 0; avg[k] = 0; min[k] = ~0L;
}
for (j = 0; j < nloop; j++) {
long diff;
struct timespec ts0[4], ts1[4];
if (send_and_stamp(sock[0], pkt, i, 0) < 0) {
fprintf(stderr, "%s: send: %s\n", argv[0],
strerror(errno));
exit(1);
}
if (get_stamp(ts0) < 0) {
fprintf(stderr, "%s: getstamp(send): %s\n",
argv[0], strerror(errno));
continue;
}
if (recv_and_stamp(sock[1], pkt, sizeof(pkt), 0) < 0) {
fprintf(stderr, "%s: recv: %s\n", argv[0],
strerror(errno));
exit(1);
}
get_stamp(ts1);
for (k = 0; 0 && k < 4; k++) {
printf("%i: %10li.%09li %10li.%09li\n", k,
ts0[k].tv_sec, ts0[k].tv_nsec,
ts1[k].tv_sec, ts1[k].tv_nsec);
}
/* collect differences for all 4 values*/
for (k = 0; k < 4; k++) {
diff = tsdiff(ts1 + k, ts0 + k);
tot[k] += diff;
if (diff < min[k]) min[k] = diff;
if (diff > max[k]) max[k] = diff;
if (0)
printf("%li%c", diff,
k == 3 ? '\n' : ' ');
}
}
for (k = 0; k < 4; k++) {
avg[k] = tot[k] / nloop;
printf("size %4i ts %i: %7li %7li %7li\n", i, k,
min[k], avg[k], max[k]);
}
}
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