2023-10-28 06:23:06 +00:00
|
|
|
/*
|
|
|
|
* mii_thread.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2023-10-28 15:59:08 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
2024-01-20 12:05:26 +00:00
|
|
|
#include <sys/timerfd.h>
|
2023-10-28 15:59:08 +00:00
|
|
|
// probably should wrap these into a HAVE_JOYSTICK define for non-linux
|
|
|
|
#ifndef HAVE_JOYSTICK
|
|
|
|
#define HAVE_JOYSTICK 1
|
|
|
|
#endif
|
|
|
|
|
2023-10-28 06:23:06 +00:00
|
|
|
#include "mii.h"
|
|
|
|
#include "mii_thread.h"
|
|
|
|
|
2024-01-20 12:05:26 +00:00
|
|
|
#include <time.h>
|
2023-10-28 06:23:06 +00:00
|
|
|
typedef uint64_t mii_time_t;
|
|
|
|
enum {
|
|
|
|
MII_TIME_RES = 1,
|
|
|
|
MII_TIME_SECOND = 1000000,
|
|
|
|
MII_TIME_MS = (MII_TIME_SECOND/1000),
|
|
|
|
};
|
|
|
|
mii_time_t
|
|
|
|
mii_get_time()
|
|
|
|
{
|
|
|
|
struct timespec tim;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &tim);
|
|
|
|
uint64_t time = ((uint64_t)tim.tv_sec) * (1000000 / MII_TIME_RES) +
|
|
|
|
tim.tv_nsec / (1000 * MII_TIME_RES);
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float default_fps = 60;
|
|
|
|
mii_th_fifo_t signal_fifo;
|
|
|
|
|
2024-01-20 12:05:26 +00:00
|
|
|
int
|
|
|
|
mii_thread_set_fps(
|
|
|
|
int timerfd,
|
|
|
|
float fps)
|
|
|
|
{
|
|
|
|
default_fps = fps;
|
|
|
|
long target_fps_us = 1000000 / default_fps;
|
|
|
|
struct itimerspec its = {
|
|
|
|
.it_interval = { .tv_sec = 0, .tv_nsec = target_fps_us * 1000 },
|
|
|
|
.it_value = { .tv_sec = 0, .tv_nsec = target_fps_us * 1000 },
|
|
|
|
};
|
|
|
|
if (timerfd_settime(timerfd, 0, &its, NULL) < 0) {
|
|
|
|
perror(__func__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-10-28 06:23:06 +00:00
|
|
|
static void *
|
2023-10-28 15:59:08 +00:00
|
|
|
mii_thread_cpu_regulator(
|
2023-10-28 06:23:06 +00:00
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
mii_t *mii = (mii_t *) arg;
|
|
|
|
uint32_t running = 1;
|
|
|
|
|
2024-01-20 12:05:26 +00:00
|
|
|
// use a timerfd as regulation
|
|
|
|
int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
|
|
|
if (timerfd < 0) {
|
|
|
|
perror(__func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
mii_thread_set_fps(timerfd, default_fps);
|
|
|
|
mii->state = MII_RUNNING;
|
2023-10-28 06:23:06 +00:00
|
|
|
uint32_t last_frame = mii->video.frame_count;
|
2024-01-20 12:05:26 +00:00
|
|
|
while (running) {
|
2023-10-28 06:23:06 +00:00
|
|
|
mii_th_signal_t sig;
|
|
|
|
while (!mii_th_fifo_isempty(&signal_fifo)) {
|
|
|
|
sig = mii_th_fifo_read(&signal_fifo);
|
|
|
|
switch (sig.cmd) {
|
|
|
|
case SIGNAL_RESET:
|
|
|
|
mii_reset(mii, sig.data);
|
|
|
|
break;
|
|
|
|
case SIGNAL_STOP:
|
|
|
|
mii_dump_run_trace(mii);
|
|
|
|
mii_dump_trace_state(mii);
|
|
|
|
mii->state = MII_STOPPED;
|
|
|
|
break;
|
|
|
|
case SIGNAL_STEP:
|
|
|
|
mii->state = MII_STEP;
|
|
|
|
running = 1;
|
|
|
|
break;
|
|
|
|
case SIGNAL_RUN:
|
|
|
|
mii->state = MII_RUNNING;
|
|
|
|
running = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mii->state != MII_STOPPED)
|
|
|
|
mii_run(mii);
|
2024-01-20 12:05:26 +00:00
|
|
|
bool sleep = false;
|
2023-10-28 06:23:06 +00:00
|
|
|
switch (mii->state) {
|
|
|
|
case MII_STOPPED:
|
2024-01-20 12:05:26 +00:00
|
|
|
sleep = true;
|
2023-10-28 06:23:06 +00:00
|
|
|
break;
|
|
|
|
case MII_STEP:
|
2024-01-20 12:05:26 +00:00
|
|
|
sleep = true;
|
2023-10-28 06:23:06 +00:00
|
|
|
if (running) {
|
|
|
|
running--;
|
|
|
|
mii_dump_trace_state(mii);
|
|
|
|
running = 1;
|
|
|
|
if (mii->trace.step_inst)
|
|
|
|
mii->trace.step_inst--;
|
|
|
|
if (mii->trace.step_inst == 0)
|
|
|
|
mii->state = MII_STOPPED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MII_RUNNING:
|
2024-01-20 12:05:26 +00:00
|
|
|
sleep = mii->video.frame_count != last_frame;
|
|
|
|
if (sleep)
|
|
|
|
last_frame = mii->video.frame_count;
|
2023-10-28 06:23:06 +00:00
|
|
|
break;
|
|
|
|
case MII_TERMINATE:
|
2024-01-20 12:05:26 +00:00
|
|
|
running = 0;
|
2023-10-28 06:23:06 +00:00
|
|
|
break;
|
|
|
|
}
|
2024-01-20 12:05:26 +00:00
|
|
|
if (sleep) {
|
|
|
|
uint64_t timer_v;
|
|
|
|
read(timerfd, &timer_v, sizeof(timer_v));
|
2023-10-28 06:23:06 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-20 12:05:26 +00:00
|
|
|
mii_dispose(mii); // this sets mii->state to MII_INIT
|
2023-10-28 06:23:06 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-10-28 15:59:08 +00:00
|
|
|
#if HAVE_JOYSTICK
|
|
|
|
#include <linux/joystick.h>
|
|
|
|
|
|
|
|
static void *
|
|
|
|
mii_thread_joystick(
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
int fd = open("/dev/input/js0", O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
|
|
printf("No joystick found\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
uint8_t axes, buttons;
|
|
|
|
if (ioctl(fd, JSIOCGAXES, &axes) == -1 ||
|
|
|
|
ioctl(fd, JSIOCGBUTTONS, &buttons) == -1) {
|
|
|
|
perror(__func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
2024-01-20 12:05:26 +00:00
|
|
|
char name[128];
|
|
|
|
if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) == -1) {
|
|
|
|
perror(__func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
printf("Joystick found: '%s'\n", name);
|
|
|
|
#if 0
|
|
|
|
printf(" %d axes, %d buttons\n", axes, buttons);
|
|
|
|
// get and print mappings
|
|
|
|
struct js_corr corr[axes] = {};
|
|
|
|
if (ioctl(fd, JSIOCGCORR, corr) == -1) {
|
|
|
|
perror(__func__);
|
|
|
|
// return NULL;
|
|
|
|
}
|
|
|
|
printf(" %d axes, %d buttons\n", axes, buttons);
|
|
|
|
for (int i = 0; i < axes; i++) {
|
|
|
|
printf("coor %d: type %d, prec %d: %d %d %d %d\n", i,
|
|
|
|
corr[i].type, corr[i].prec,
|
|
|
|
corr[i].coef[0], corr[i].coef[1],
|
|
|
|
corr[i].coef[2], corr[i].coef[3]);
|
|
|
|
}
|
|
|
|
printf("Joystick thread started: %d axis, %d buttons\n", axes, buttons);
|
|
|
|
#endif
|
2023-10-28 15:59:08 +00:00
|
|
|
struct js_event event;
|
|
|
|
mii_t *mii = (mii_t *)arg;
|
|
|
|
mii->analog.v[0].value = 127;
|
|
|
|
mii->analog.v[1].value = 127;
|
|
|
|
do {
|
|
|
|
ssize_t rd = read(fd, &event, sizeof(event));
|
|
|
|
if (rd != sizeof(event)) {
|
|
|
|
perror(__func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (event.type) {
|
|
|
|
case JS_EVENT_BUTTON:
|
|
|
|
// printf("button %u %s\n", event.number, event.value ? "pressed" : "released");
|
|
|
|
switch (event.number) {
|
|
|
|
case 2 ... 3:
|
|
|
|
mii_bank_poke(&mii->bank[MII_BANK_MAIN],
|
|
|
|
0xc061 + (event.number - 2),
|
|
|
|
event.value ? 0x80 : 0);
|
|
|
|
break;
|
|
|
|
case 4 ... 5:
|
|
|
|
mii_bank_poke(&mii->bank[MII_BANK_MAIN],
|
|
|
|
0xc061 + (event.number - 4),
|
|
|
|
event.value ? 0x80 : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case JS_EVENT_AXIS:
|
|
|
|
switch (event.number) {
|
|
|
|
case 0 ... 1: {// X
|
|
|
|
uint32_t v = (event.value + 0x8000) / 256;
|
|
|
|
if (v > 255)
|
|
|
|
v = 255;
|
|
|
|
mii->analog.v[event.number ? 1 : 0].value = v;
|
|
|
|
// printf("axis %u %6d %3dx%3d\n"
|
|
|
|
// event.number, event.value,
|
|
|
|
// mii->analog.v[0].value, mii->analog.v[1].value);
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Ignore init events. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
close(fd);
|
|
|
|
printf("Joystick thread terminated\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-01-20 12:05:26 +00:00
|
|
|
pthread_t
|
2023-10-28 06:23:06 +00:00
|
|
|
mii_thread_start(
|
|
|
|
mii_t *mii)
|
|
|
|
{
|
|
|
|
const mii_th_fifo_t zero = {};
|
|
|
|
signal_fifo = zero;
|
2023-10-28 15:59:08 +00:00
|
|
|
|
2024-01-20 12:05:26 +00:00
|
|
|
pthread_t cpu, joystick;
|
|
|
|
pthread_create(&cpu, NULL, mii_thread_cpu_regulator, mii);
|
2023-10-28 15:59:08 +00:00
|
|
|
#if HAVE_JOYSTICK
|
2024-01-20 12:05:26 +00:00
|
|
|
pthread_create(&joystick, NULL, mii_thread_joystick, mii);
|
2023-10-28 15:59:08 +00:00
|
|
|
#endif
|
2024-01-20 12:05:26 +00:00
|
|
|
return cpu;
|
2023-10-28 06:23:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct mii_th_fifo_t*
|
|
|
|
mii_thread_get_fifo(
|
|
|
|
struct mii_t *mii)
|
|
|
|
{
|
|
|
|
return &signal_fifo;
|
|
|
|
}
|