+/*******************************************************/
+/* Simple vcan latency benchmark */
+/* Copyright (C) 2012 Michal Sojka, DCE FEE CTU Prague */
+/* License: GPLv2 */
+/*******************************************************/
+
+#include <errno.h>
+#include <error.h>
+#include <linux/can.h>
+#include <linux/can/raw.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#define MEMSET_ZERO(obj) memset(&(obj), 0, sizeof(obj))
+
+static inline int sock_get_if_index(int s, const char *if_name)
+{
+ struct ifreq ifr;
+ MEMSET_ZERO(ifr);
+
+ strcpy(ifr.ifr_name, if_name);
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0)
+ error(1, errno, "SIOCGIFINDEX '%s'", if_name);
+ return ifr.ifr_ifindex;
+}
+
+int create_can_socket(const char *dev)
+{
+ int s;
+ struct sockaddr_can addr;
+ if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
+ error(1, errno, "socket");
+
+ addr.can_family = AF_CAN;
+ addr.can_ifindex = sock_get_if_index(s, dev);
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ error(1, errno, "bind");
+
+ return s;
+}
+
+/* Subtract the `struct timespec' values X and Y, storing the result in
+ RESULT. Return 1 if the difference is negative, otherwise 0. */
+
+int timespec_subtract (struct timespec *result, const struct timespec *x, const struct timespec *yy)
+{
+ struct timespec ylocal = *yy, *y = &ylocal;
+ /* Perform the carry for the later subtraction by updating Y. */
+ if (x->tv_nsec < y->tv_nsec) {
+ int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
+ y->tv_nsec -= 1000000000 * nsec;
+ y->tv_sec += nsec;
+ }
+ if (x->tv_nsec - y->tv_nsec > 1000000000) {
+ int nsec = (x->tv_nsec - y->tv_nsec) / 1000000000;
+ y->tv_nsec += 1000000000 * nsec;
+ y->tv_sec -= nsec;
+ }
+
+ /* Compute the time remaining to wait.
+ `tv_nsec' is certainly positive. */
+ result->tv_sec = x->tv_sec - y->tv_sec;
+ result->tv_nsec = x->tv_nsec - y->tv_nsec;
+
+ /* Return 1 if result is negative. */
+ return x->tv_sec < y->tv_sec;
+}
+
+unsigned long timespec_diff_ns(const struct timespec *x, const struct timespec *y)
+{
+ struct timespec r;
+ timespec_subtract(&r, x, y);
+ return r.tv_sec*1000000000 + r.tv_nsec;
+}
+
+#define CHKERR(expr) ({ int __ret = (expr); if (__ret == -1) error(1, errno, #expr); __ret; })
+
+static int cmp(const void *p1, const void *p2)
+{
+ return *(unsigned long*)p1 - *(unsigned long*)p2;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *dev;
+ long count;
+ int s1, s2;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <dev> [ <frame_count> ]\n", argv[0]);
+ exit(1);
+ }
+ dev = argv[1];
+ count = argc > 2 ? atol(argv[2]) : -1;
+
+ CHKERR(s1 = create_can_socket(dev));
+ CHKERR(s2 = create_can_socket(dev));
+
+ unsigned long *ns = NULL;
+
+ if (count > 0)
+ ns = malloc(count*sizeof(*ns));
+
+ unsigned long i;
+ for (i = 0; count == -1 || i < count; i++) {
+ struct can_frame f1 = { .can_id = 0x123, .can_dlc = 8 }, f2;
+ struct timespec t1, t2;
+
+ clock_gettime(CLOCK_MONOTONIC, &t1);
+ CHKERR(write(s1, &f1, sizeof(f1)));
+ CHKERR(read(s2, &f2, sizeof(f2)));
+ clock_gettime(CLOCK_MONOTONIC, &t2);
+ unsigned long delay = timespec_diff_ns(&t2, &t1);
+ if (count >= 0) {
+ ns[i] = delay;
+ } else
+ printf("delay %lu ns\n", delay);
+ }
+
+ if (ns) {
+ unsigned long long sum = 0;
+ for (i = 0; i < count; i++)
+ sum += ns[i];
+ printf("avg=%lld [ns]\n", sum/count);
+
+ qsort(ns, count, sizeof(*ns), cmp);
+ printf("percentiles: 0th:%ld 25th:%d 50th:%d 75th:%d 100th:%d\n",
+ ns[0], ns[count/4], ns[count/2], ns[count*3/4], ns[count-1]);
+ }
+
+ return 0;
+}