/*
 * probe2.c - DF-5707: single forced-state sendto with us-resolution randseed
 * polling around it. Prints a timeline of every randseed change.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>

#define RSPHYS 0x1120c80

static uint64_t rrs(int fd) { uint64_t v=0; if (pread(fd,&v,8,RSPHYS)!=8){perror("pread");exit(1);} return v; }

static double now(struct timespec *t0)
{
    struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
    return (ts.tv_sec - t0->tv_sec) + 1e-9*(ts.tv_nsec - t0->tv_nsec);
}

static unsigned short cksum(unsigned short *p, int n)
{
    unsigned long s = 0;
    while (n > 1) { s += *p++; n -= 2; }
    if (n) s += *(unsigned char *)p;
    s = (s >> 16) + (s & 0xffff); s += (s >> 16);
    return ~s;
}

int main(int argc, char **argv)
{
    int mfd = open("/dev/mem", O_RDONLY);
    if (mfd < 0) { perror("/dev/mem"); return 1; }
    struct timespec t0; clock_gettime(CLOCK_MONOTONIC, &t0);

    int s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (s < 0) { perror("socket"); return 1; }
    struct sockaddr_in dst; memset(&dst,0,sizeof(dst));
    dst.sin_family=AF_INET; dst.sin_addr.s_addr=inet_addr("127.0.0.1");

    char pkt[64]; memset(pkt,0,sizeof(pkt));
    struct icmp *icp=(struct icmp*)pkt;
    icp->icmp_type=ICMP_ECHO; icp->icmp_code=0; icp->icmp_id=htons(0x5708);
    icp->icmp_seq=htons(1); icp->icmp_cksum=0;
    icp->icmp_cksum=cksum((unsigned short*)pkt,64);

    /* pre-poll 5ms to catch pre-send activity */
    uint64_t prev = rrs(mfd);
    printf("t=%.6f rs=%llu (pre)\n", now(&t0), (unsigned long long)prev);
    while (now(&t0) < 0.005) {
        uint64_t v = rrs(mfd);
        if (v != prev) { printf("t=%.6f rs=%llu\n", now(&t0), (unsigned long long)v); prev=v; }
    }
    int r = sendto(s, pkt, 64, 0, (struct sockaddr*)&dst, sizeof(dst));
    printf("t=%.6f sendto=%d errno=%d rs=%llu\n", now(&t0), r, errno, (unsigned long long)rrs(mfd));
    /* post-poll 30ms */
    while (now(&t0) < 0.035) {
        uint64_t v = rrs(mfd);
        if (v != prev) { printf("t=%.6f rs=%llu\n", now(&t0), (unsigned long long)v); prev=v; }
    }
    /* drain any reply */
    char buf[128]; struct sockaddr_in from; socklen_t fl=sizeof(from);
    struct timeval tv={0,200000};
    setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    ssize_t rr = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&from, &fl);
    printf("t=%.6f recv=%zd rs=%llu\n", now(&t0), rr, (unsigned long long)rrs(mfd));
    while (now(&t0) < 0.100) {
        uint64_t v = rrs(mfd);
        if (v != prev) { printf("t=%.6f rs=%llu\n", now(&t0), (unsigned long long)v); prev=v; }
    }
    return 0;
}
