/* DF-5100 trigger — exact source built on the audit guest (see build.log).
 *
 * joyread() spins inside crit_enter() until WALL-CLOCK nanotime() passes
 * `start + timeout` (sys/dev/misc/joy/joy.c:190-213).  nanotime() adds the
 * stepped basetime (sys/kern/kern_clock.c:1562-1587), so a backward clock
 * step (settimeofday(2) / NTP step) during the loop extends the deadline in
 * wall time and keeps the calling CPU in a critical section (interrupts
 * masked, preemption blocked) for far longer than the configured timeout.
 *
 * This trigger pushes JOY_SETTIMEOUT to the 10000us maximum (joy.c:243)
 * and times back-to-back reads.  On hardware WITH /dev/joy0 each read
 * measures ~timeout us of interrupt-masked spin; paired with a concurrent
 * backward settimeofday() the read stalls ~step-seconds longer.
 * On this audit guest joy(4) never attaches (no ISA gameport; kldload joy
 * alone creates no node), so open() returns ENOENT (see run.log).
 */
#include <sys/ioctl.h>
#include <sys/joystick.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#define DEV "/dev/joy0"
int main(void) {
    struct joystick j; struct timeval t0, t1;
    int fd, i, tmo = 10000;
    fd = open(DEV, O_RDWR);
    if (fd < 0) {
        printf("open %s: %s (errno=%d)\n", DEV, strerror(errno), errno);
        printf("joy(4) not attached on this machine -- cannot exercise joyread() crit loop\n");
        return (2);
    }
    if (ioctl(fd, JOY_SETTIMEOUT, &tmo) < 0) { printf("JOY_SETTIMEOUT: %s\n", strerror(errno)); return (1); }
    for (i = 0; i < 5; i++) {
        gettimeofday(&t0, NULL);
        if (read(fd, &j, sizeof(j)) < 0) { printf("read: %s\n", strerror(errno)); return (1); }
        gettimeofday(&t1, NULL);
        printf("read %d: %ld us (x=%d y=%d b1=%d b2=%d)\n", i,
            (t1.tv_sec - t0.tv_sec) * 1000000L + (t1.tv_usec - t0.tv_usec), j.x, j.y, j.b1, j.b2);
    }
    return (0);
}
