0.2.2: read /dev/klog directly for sign-in again

On the console, 0.2.1 often could not add a controller ("Cannot read
controller sign-in events"). Its log shows why: once Control4Free let go
of GoldHEN's klog stream, every new connection read nothing for 4-5
minutes. GoldHEN's klog server serves one client at a time and is slow to
notice that one has left.

The browser service now opens /dev/klog itself, only while a controller
waits for sign-in, and releases it afterwards, so GoldHEN's klog server
works the rest of the time. It never uses GoldHEN's stream; only the
diagnostic stages still fall back to it.

Version 0.2.2 (APP_VER 00.22).
This commit is contained in:
MoHadiShibli committed 2026-10-05 00:03:34 +03:00
1 parent 5611a55075
commit f008db6e16
7 files changed
+60 -43

No files matched your search

+14 -13
View File
@@ -5,10 +5,10 @@ keyboard, or an Xbox/other controller exposed by the browser's Gamepad API.
Virtual controllers use the PS4's native user-selection screen, so they work on
the home screen, at sign-in and in games.
**Status: browser control, native user sign-in, gameplay, the launcher and
GoldHEN AutoRun have been confirmed on the development console.** Version 0.2.1
adds network recovery and changes klog ownership; these changes pass local tests
and still need a console rest/wake test. Four virtual slots are exposed; the
**Status: browser control, native user sign-in, gameplay, the launcher,
GoldHEN AutoRun and recovery after rest mode have been confirmed on the
development console.** Version 0.2.2 reads the kernel log directly again for
sign-in; it still needs its console check. Four virtual slots are exposed; the
console's device limits apply.
## Requirements
@@ -25,7 +25,7 @@ console's device limits apply.
## Install
1. Copy `build/Control4Free-0.2.1.pkg` to a USB drive (or to `/data/pkg/` over
1. Copy `build/Control4Free-0.2.2.pkg` to a USB drive (or to `/data/pkg/` over
GoldHEN's FTP server) and install it with GoldHEN's Package Installer.
2. Open **Control4Free** from the home screen and press **Cross** once. The app
adds Control4Free to GoldHEN's AutoRun (GoldHEN 2.4b18.10 or later), so GoldHEN
@@ -63,7 +63,7 @@ When you update Control4Free this way, replace `/data/payloads/control4free.elf`
controller page's menu, or restart the PS4. That old version has no launcher API,
so the app will not start a second copy over it.
**Rest mode (0.2.1):** the service now closes stale connections and rebuilds its
**Rest mode (since 0.2.1):** the service closes stale connections and rebuilds its
listener after socket failures or a long pause. Queued input is discarded, and
disconnected controllers report neutral input. Reopen the controller page and
select your controller after waking; an unused controller is removed after the
@@ -71,12 +71,13 @@ reconnect grace period. This can recover a surviving payload's network service;
it cannot revive a host process that the console terminated or stopped running.
When upgrading a stuck older instance, restart the PS4 and load GoldHEN again.
**Kernel logs:** Control4Free requests a verified stream from GoldHEN first,
falling back to `/dev/klog`. It only keeps the reader while controllers are
awaiting native sign-in, and releases it once sign-in finishes or those
controllers are removed. Close other klog viewers when adding a controller:
GoldHEN versions that serve one reader cannot share that capture window. A busy
reader now produces a retryable error before a virtual device is created.
**Kernel logs:** controller sign-in is detected in the PS4's kernel log, which
has a single reader. Control4Free opens `/dev/klog` only while a controller is
waiting for sign-in and releases it afterwards, so GoldHEN's klog server works
the rest of the time. It does not use GoldHEN's klog stream: GoldHEN serves one
client at a time and, after one leaves, can go minutes without serving the next.
If a klog viewer is connected to GoldHEN when you add a controller, the page
says so; close the viewer and try again.
Diagnostics are saved at `/data/control4free/spike.log`, with elapsed timestamps
such as `[c4f] [+00:01:23.456]`; the PS4's calendar setting is not used. Starting
@@ -150,7 +151,7 @@ docker build -t control4free-launcher-build -f docker/Dockerfile.launcher docker
docker run --rm -v "$PWD:/src" -w /src control4free-launcher-build bash -lc 'make && make -C launcher'
```
Output: `build/Control4Free-0.2.1.pkg` (title ID `CFRE00001`). On Windows,
Output: `build/Control4Free-0.2.2.pkg` (title ID `CFRE00001`). On Windows,
`tools/build-launcher.ps1` runs both image builds and the package build. It does
not send anything to a console. Packaging uses OpenOrbis and LibOrbisPkg. The
launcher draws its screen and its icon in software, in the controller page's style.
+4 -2
View File
@@ -99,8 +99,10 @@ void c4fProbeButtonMap(const C4fVirtualPad *pad, int32_t readHandle);
* already held by that server. */
#define C4F_KLOG_PORT 3232
/* Verified GoldHEN socket first, /dev/klog fallback; -1 if neither delivers
* a fresh marker. The caller must release it when sign-in capture finishes. */
/* /dev/klog only, verified by a fresh marker; -1 if busy or silent. For the
* browser service, which holds it only while a controller signs in. */
int c4fKlogOpenDevice(void);
/* /dev/klog, else GoldHEN's stream on 3232 (diagnostic stages); verified. */
int c4fKlogOpen(void);
void c4fKlogDrain(int fd);
/* Writes a marker to klog and reads it back. 0 = the capture path works. */
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef C4F_LAUNCHER_SCREEN_H
#define C4F_LAUNCHER_SCREEN_H
#include <stdint.h>
#define C4F_LAUNCHER_VERSION "0.2.1"
#define C4F_LAUNCHER_VERSION "0.2.2"
#define C4F_SCREEN_WIDTH 1920
#define C4F_SCREEN_HEIGHT 1080
typedef struct {
+34 -20
View File
@@ -238,28 +238,42 @@ fail:
return -1;
}
/* Drained and proven by a marker that comes back, or closed. */
static int c4fKlogVerified(int fd, const char *what)
{
c4fKlogDrain(fd);
if (c4fKlogSelfTest(fd) == 0) {
c4fLog("reading klog through %s\n", what);
return fd;
}
close(fd);
return -1;
}
/* GoldHEN's klog server opens /dev/klog only while it has a client and serves
* one client at a time. After a client leaves it can go minutes without
* serving the next one: on the console (2026-10-04) every reconnect after a
* release read 0 bytes for 4-5 minutes. So the browser service never uses the
* stream; it reads the device itself, only while a controller signs in. */
int c4fKlogOpenDevice(void)
{
int fd = open("/dev/klog", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
c4fLog("open(/dev/klog) failed errno=%d (16: GoldHEN's klog server has a client)\n", errno);
return -1;
}
return c4fKlogVerified(fd, "/dev/klog");
}
int c4fKlogOpen(void)
{
int fd = c4fKlogConnectLocal();
if (fd >= 0) {
c4fKlogDrain(fd);
if (c4fKlogSelfTest(fd) == 0) {
c4fLog("reading klog through 127.0.0.1:%d\n", C4F_KLOG_PORT);
return fd;
}
close(fd);
c4fLog("klog socket has no live stream; another log client may own it\n");
}
fd = open("/dev/klog", O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
c4fKlogDrain(fd);
if (c4fKlogSelfTest(fd) == 0) {
c4fLog("temporarily reading /dev/klog for controller sign-in\n");
return fd;
}
close(fd);
}
c4fLog("no verified klog source; close other log viewers before adding a controller\n");
int fd = c4fKlogOpenDevice();
if (fd >= 0) return fd;
/* The diagnostic stages run once, usually with the PC's klog viewer
* attached, which holds the device: then the stream is the only way. */
fd = c4fKlogConnectLocal();
if (fd >= 0 && (fd = c4fKlogVerified(fd, "127.0.0.1:3232")) >= 0) return fd;
c4fLog("no verified klog source\n");
return -1;
}
+3 -3
View File
@@ -15,7 +15,7 @@
#include "c4f_vda.h"
#include "c4f_web.h"
#define C4F_VERSION "0.2.1"
#define C4F_VERSION "0.2.2"
#define C4F_INPUT_MASK 0x0011ffffu
#define C4F_REPORT_MS 16
#ifndef C4F_STALE_MS
@@ -257,8 +257,8 @@ static void c4fClaim(C4fWeb *app, C4fNetClient *c, C4fRequest *r)
c4fReadKlog(app);
if (app->klogFd >= 0 && c4fKlogSelfTest(app->klogFd)) c4fReleaseKlog(app);
}
if (app->klogFd < 0) app->klogFd = c4fKlogOpen();
if (app->klogFd < 0 && needsDevice) { c4fError(c, r, 503, "Cannot read controller sign-in events. Close other klog viewers and try again."); return; }
if (app->klogFd < 0) app->klogFd = c4fKlogOpenDevice();
if (app->klogFd < 0 && needsDevice) { c4fError(c, r, 503, "Cannot read the PS4's kernel log, which sign-in needs. If a klog viewer is connected to GoldHEN, close it and try again."); return; }
}
for (int i = 0; i < C4F_MAX_PADS; i++) if ((wanted & (1u << i)) && !app->pads[i].active) {
C4fWebPad *p = &app->pads[i];
+1 -1
View File
@@ -9,7 +9,7 @@ try {
if ($LASTEXITCODE -ne 0) { throw 'Launcher toolchain build failed.' }
docker run --rm --network none -v "${projectRoot}:/src" -w /src control4free-launcher-build bash -lc 'make && make -C launcher'
if ($LASTEXITCODE -ne 0) { throw 'Launcher package build failed.' }
Get-Item -LiteralPath (Join-Path $projectRoot 'build/Control4Free-0.2.1.pkg') | Select-Object FullName,Length
Get-Item -LiteralPath (Join-Path $projectRoot 'build/Control4Free-0.2.2.pkg') | Select-Object FullName,Length
} finally {
Pop-Location
}
+3 -3
View File
@@ -93,11 +93,11 @@ def main():
sfo = 'sce_sys/param.sfo'
run(pkgtool, 'sfo_new', sfo)
entries = {
'APP_TYPE': ('Integer', 4, '1'), 'APP_VER': ('Utf8', 8, '00.21'),
'APP_TYPE': ('Integer', 4, '1'), 'APP_VER': ('Utf8', 8, '00.22'),
'ATTRIBUTE': ('Integer', 4, '0'), 'CATEGORY': ('Utf8', 4, 'gd'),
'CONTENT_ID': ('Utf8', 48, CONTENT_ID), 'DOWNLOAD_DATA_SIZE': ('Integer', 4, '0'),
'SYSTEM_VER': ('Integer', 4, '0'), 'TITLE': ('Utf8', 128, 'Control4Free'),
'TITLE_ID': ('Utf8', 12, TITLE_ID), 'VERSION': ('Utf8', 8, '00.21'),
'TITLE_ID': ('Utf8', 12, TITLE_ID), 'VERSION': ('Utf8', 8, '00.22'),
}
for key, (kind, size, value) in entries.items():
run(pkgtool, 'sfo_setentry', sfo, key, '--type', kind, '--maxsize', size, '--value', value)
@@ -123,7 +123,7 @@ def main():
shutil.copyfile(project, OUT / 'launcher.gp4')
run(pkgtool, 'pkg_build', project, OUT)
generated = OUT / (CONTENT_ID + '.pkg')
target = ROOT / 'build/Control4Free-0.2.1.pkg'
target = ROOT / 'build/Control4Free-0.2.2.pkg'
shutil.copyfile(generated, target)
print(f'Built {target} ({target.stat().st_size:,} bytes)', flush=True)