mirror of
https://github.com/MoHadiShibli/Control4Free.git
synced 2026-10-06 09:00:29 +02:00
The staged spike that worked out the VDA call order is no longer part of the build. It is kept at tag spike-final. - src/main.c is the service only: instance lock, credentials, MBus and pad init, then the web service. src/server.c and include/c4f_server.h are gone with stage 8, and so are the stage-only helpers in src/vda.c: the MBus bind and holds, the assignment and button-map probes, and the GoldHEN klog stream path. The service reads /dev/klog, which is what works. - VERSION holds the only version number. Both Makefiles pass it in, the packager reads it for param.sfo and the package name, and the build and deploy scripts read it for the file they look for. - The payload's log is /data/control4free/control4free.log, not spike.log. - The page drops the protocol 1 branches: the payload has always answered 2. A console slot is a "controller" and a pad plugged into the phone or PC is a "gamepad", so the two no longer read as the same word. - The host tests move to tests/, with tests/run.py to run all four suites. They build the real sources with the PS4 calls stubbed, so they need no console. klog_test now covers the device path only, and asserts that nothing ever connects to GoldHEN's klog server.
83 lines
2.9 KiB
C
83 lines
2.9 KiB
C
/* The real VDA capture functions, with only /dev/klog and the klog writer
|
|
* stubbed. Modes: direct (the device is free), busy (another reader has it).
|
|
*
|
|
* GoldHEN's klog server on port 3232 serves one client at a time and can go
|
|
* minutes without serving the next one, so the service must never reach for it.
|
|
* A listener on 3232 stays open through both modes and must never see a
|
|
* connection. */
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netinet/in.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include "c4f_vda.h"
|
|
|
|
static atomic_int peer = -1;
|
|
static int direct[2] = {-1, -1}, enabled = 1, deviceBusy, directOpens, waits;
|
|
static void waitCallback(void *unused) { (void)unused; waits++; }
|
|
void c4fLog(const char *fmt, ...) { (void)fmt; }
|
|
int c4fLogKlogEnabled(void) { return enabled; }
|
|
void c4fLogSetKlog(int value) { enabled = value; }
|
|
int klog_printf(const char *fmt, ...)
|
|
{
|
|
char line[1024]; va_list ap;
|
|
va_start(ap, fmt); int n = vsnprintf(line, sizeof(line), fmt, ap); va_end(ap);
|
|
if (direct[1] >= 0) { assert(write(direct[1], line, n) == n); return n; }
|
|
return n;
|
|
}
|
|
int __real_open(const char *, int, ...);
|
|
int __wrap_open(const char *path, int flags, ...)
|
|
{
|
|
if (strcmp(path, "/dev/klog")) return __real_open(path, flags);
|
|
directOpens++;
|
|
if (deviceBusy) { errno = EBUSY; return -1; }
|
|
assert(pipe(direct) == 0);
|
|
assert(fcntl(direct[0], F_SETFL, O_NONBLOCK) == 0);
|
|
return direct[0];
|
|
}
|
|
static void *serve(void *arg)
|
|
{
|
|
int listener = *(int *)arg;
|
|
peer = accept(listener, NULL, NULL);
|
|
return NULL;
|
|
}
|
|
int main(int argc, char **argv)
|
|
{
|
|
assert(argc == 2);
|
|
const char *mode = argv[1];
|
|
int listener, fd; pthread_t thread;
|
|
deviceBusy = strcmp(mode, "direct") != 0;
|
|
|
|
listener = socket(AF_INET, SOCK_STREAM, 0);
|
|
int one = 1; setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
|
struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(3232), .sin_addr.s_addr = htonl(INADDR_LOOPBACK)};
|
|
assert(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) == 0);
|
|
assert(listen(listener, 1) == 0);
|
|
assert(pthread_create(&thread, NULL, serve, &listener) == 0);
|
|
c4fVdaSetWaitCallback(waitCallback, NULL);
|
|
|
|
fd = c4fKlogOpenDevice();
|
|
if (!deviceBusy) {
|
|
/* Non-blocking, opened once, and verified by a marker read back, which
|
|
* the wait callback keeps existing controllers reporting through. */
|
|
assert(fd >= 0 && (fcntl(fd, F_GETFL) & O_NONBLOCK) && directOpens == 1 && waits > 0);
|
|
close(fd);
|
|
} else {
|
|
assert(fd == -1 && directOpens == 1);
|
|
}
|
|
usleep(50000);
|
|
assert(peer < 0); /* GoldHEN's klog stream was never touched */
|
|
assert(enabled == 1);
|
|
|
|
pthread_cancel(thread);
|
|
pthread_join(thread, NULL);
|
|
close(listener);
|
|
printf("PASS real klog acquisition: %s\n", mode);
|
|
}
|