diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f21a9ae --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: mohadishibli diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..3ca050f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,49 @@ +name: Bug report +description: Something doesn't work as it should +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for reporting! First, please check the [troubleshooting guide](https://github.com/MoHadiShibli/Control4Free/blob/main/docs/troubleshooting.md): most setup problems are covered there. + - type: input + id: version + attributes: + label: Control4Free version + description: Shown at the bottom of the controller page and of the app, for example "Control4Free 1.0.0". + validations: + required: true + - type: input + id: console + attributes: + label: Firmware and GoldHEN version + placeholder: "Firmware 10.01, GoldHEN v2.4b18.10" + validations: + required: true + - type: input + id: client + attributes: + label: Phone or computer, and browser + placeholder: "Android phone, Chrome" + validations: + required: true + - type: input + id: where + attributes: + label: Where it happened + description: The home screen, the sign-in screen, a game (name and title ID, like CUSA00001), or the app. + - type: textarea + id: what + attributes: + label: What happened? + description: What did you do, what did you expect, and what happened instead? + validations: + required: true + - type: textarea + id: log + attributes: + label: Log (optional) + description: | + `/data/control4free/control4free.log` on the PS4, over GoldHEN's FTP server (port 2121). If the problem was before a restart, it's in `control4free.log.previous`. + The log contains your console's network address: remove it if you prefer. + render: text diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..a5c2a35 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Installation and troubleshooting + url: https://github.com/MoHadiShibli/Control4Free/blob/main/docs/troubleshooting.md + about: Setting up, playing, and fixes for the most common problems. + - name: Report a security problem + url: https://github.com/MoHadiShibli/Control4Free/security + about: Please report vulnerabilities privately, not in a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..9c77659 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,16 @@ +name: Feature request +description: An idea to make Control4Free better +labels: [enhancement] +body: + - type: textarea + id: idea + attributes: + label: What would you like? + description: Describe the idea and the problem it solves. + validations: + required: true + - type: textarea + id: games + attributes: + label: Games it matters for (optional) + description: Game names and title IDs, if it's about specific games. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4487e0c..79c78a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,4 +56,9 @@ jobs: if: startsWith(github.ref, 'refs/tags/v') env: GH_TOKEN: ${{ github.token }} - run: gh release create "${GITHUB_REF#refs/tags/}" artifacts/* --verify-tag --notes-file CHANGELOG.md + run: | + # The release notes are this version's section of the changelog. + version=$(cat VERSION) + awk -v head="## $version " 'index($0, head) == 1 { keep = 1; next } /^## / { keep = 0 } keep' CHANGELOG.md > notes.md + test -s notes.md || { echo "CHANGELOG.md has no section for $version"; exit 1; } + gh release create "${GITHUB_REF#refs/tags/}" artifacts/* --verify-tag --title "Control4Free $version" --notes-file notes.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 00cdff3..961dd98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,32 +1,29 @@ # Changelog -Dates are the day the work landed. Versions follow [Semantic Versioning](https://semver.org): -PATCH for fixes, MINOR for features that break nothing, MAJOR for anything people -rely on that changes. +Versions follow [Semantic Versioning](https://semver.org): PATCH for fixes, MINOR for features that break +nothing, MAJOR for anything people rely on that changes. -## 1.0.0 — unreleased +## 1.0.0 — 2026-10-05 First release. -Control4Free is a GoldHEN payload that turns a phone or PC into a PS4 controller -that works everywhere: the home screen, the sign-in screen and games. Up to four -of them, through the PS4's own virtual-device API, with the PS4's own -"Who's using this controller?" screen deciding who each one is. +Control4Free is a GoldHEN payload that turns a phone or PC into a PS4 controller that works everywhere: the +home screen, the sign-in screen and games. Up to four of them, through the PS4's own virtual-device API, with +the PS4's own *"Who's using this controller?"* screen deciding who each one is. -- A controller page the payload serves itself on port 4264. Touch controls shaped - like a DualShock 4, keyboard keys, or any gamepad the browser exposes. A layout - editor, key remapping and a per-controller picker, saved per device. -- An installable app for the PS4's home screen that starts and stops Control4Free, - shows its address and a QR code, and sets up GoldHEN's AutoRun so the console - starts it by itself after every restart. +- A controller page the service serves itself on port 4264: a DualShock 4 on the screen, keyboard keys, or any + gamepad the browser can see. A layout editor, key remapping and a per-gamepad controller picker, saved on + each device. +- An app for the PS4's home screen, under Applications, that starts and stops Control4Free, shows its address + and a QR code and the number of connected controllers, and sets up GoldHEN's AutoRun so the console starts + it by itself after every restart. - The page can be kept on a phone's home screen. -- Input is reported the moment it arrives, at a DualShock 4's own 4 ms cadence - while a player is doing something, and at a slow keepalive while nobody is. +- Input is reported the moment it arrives, at a DualShock 4's own 4 ms rate while a player is doing something, + and at a slow keepalive while nobody is. - Connecting a controller never stops the other players being served. -- Recovers by itself after rest mode or a network failure: controllers have to be - picked again, which is what rest mode does to real ones too. -- Open on the local network by design, with no pairing. What that does and does - not protect is written down in [SECURITY.md](SECURITY.md). +- Recovers by itself after rest mode or a network failure. Controllers have to be picked again after rest + mode, which signs every user out. +- Open on the local network by design, with no pairing. What that does and does not protect is written down in + [SECURITY.md](SECURITY.md). -Tested on firmware 10.01 with GoldHEN 2.4b18.10. Rumble, light bar and motion are -not implemented. +Tested on firmware 10.01 with GoldHEN v2.4b18.10. Rumble, light bar and motion are not passed on yet. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..f4fc5ac --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# Contributing to Control4Free + +Thanks for helping! Bug reports, compatibility reports, fixes and reviews are all welcome. + +## Reporting a bug + +Use the [bug report form](https://github.com/MoHadiShibli/Control4Free/issues/new/choose). The most useful +details: +- the Control4Free version, shown at the bottom of the page and of the app; +- the firmware and GoldHEN versions; +- the phone or computer and browser you used; +- what you did and what happened; +- the service's log, if you can get it: `/data/control4free/control4free.log` on the PS4, over GoldHEN's FTP + server (port 2121). The previous run is kept in `control4free.log.previous`. + +The log contains your console's network address. Remove it before posting if you prefer. + +Security problems: please follow [SECURITY.md](SECURITY.md) instead of opening a public issue. + +## Building + +Everything builds in Docker, so Docker is all you need: + +```shell +git clone https://github.com/MoHadiShibli/Control4Free +cd Control4Free +docker build -t control4free-build docker/ +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' +``` + +- `build/control4free.elf` is the service, with the page and its icon built in. +- `build/Control4Free-.pkg` is the app, with its own copy of the service. + +The first image pins the [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) release that builds the +service; the second adds the [OpenOrbis toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain), +pinned by digest, for the app and the package. On Windows, `tools/build-launcher.ps1` runs all three steps. + +The version comes from the `VERSION` file and nowhere else: the Makefiles and the packager read it, and +the page and the app show the version the service reports. + +## Testing + +```shell +docker run --rm --network none -v "$PWD:/src" -w /src control4free-launcher-build python3 -B tests/run.py +``` + +The suites build the real sources for the host with only the PS4 calls stubbed, so they need no console: +- `test_web` and `test_recovery` run the actual service against a fake kernel log + (`tests/web_stub.c`) that writes the same lines firmware 10.01 does, over real HTTP and WebSocket + connections; +- `test_frontend` runs the page's own functions under Node; +- `test_launcher` drives the app's logic, renders every state of its screen to `build/launcher-preview-*.png`, + and checks the package's `param.sfo`; +- `test_logging` covers the log, the kernel-log opener and DeviceId matching. + +Add a test with your fix: one that fails without it. Then test on a real console if you can, and say in your +pull request which firmware, GoldHEN version and games you tried. + +To try changes to the page without rebuilding, open `client/index.html` from disk and enter the console's +address, or add `?host=:4264` to the page's URL. + +## Code style + +Match the code around your change: +- C with GNU extensions (gnu11), 4-space indent, `camelCase` functions with a `c4f` prefix, `C4f` types and + `C4F_` macros; +- log with `c4fLog`. `c4fNotify` shows a notification on the TV: keep those rare; +- comments explain *why*, not *what*; +- only button bits confirmed on hardware go in `include/c4f_sce.h`; +- the page is one file with no build step and no libraries: keep it that way; +- OpenOrbis's headers give `MSG_NOSIGNAL`, `SIGSYS` and `CLOCK_MONOTONIC` their Linux values, which the PS4 + reads as something else. Don't use them directly in `launcher/`; a test checks. + +Build without new warnings; the app is built with `-Werror`. + +## Releasing + +1. Update `VERSION`, and add a section to `CHANGELOG.md`. +2. Commit, tag `vX.Y.Z`, and push the tag. +3. CI builds both images, runs the tests, and publishes the release with the ELF, the package and + `SHA256SUMS`. It refuses a tag that doesn't match `VERSION`. + +## Reviewing AI-written code + +Much of Control4Free was written with the help of AI models (Claude by Anthropic and Codex by OpenAI). It has +been tested, on the console as well, but AI-written code can look right and still be wrong. Please read it +critically: if you find dead code, wrong assumptions or needless complexity, +[open an issue](https://github.com/MoHadiShibli/Control4Free/issues) or send a pull request. + +## License + +Contributions are accepted under the [GNU General Public License v3](LICENSE), the project's license. diff --git a/README.md b/README.md index a9e4bc7..9c945c7 100644 --- a/README.md +++ b/README.md @@ -1,231 +1,133 @@ -# Control4Free +

Control4Free

-A GoldHEN payload for controlling the PS4 from a phone or PC, using touch, -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. +

Control4Free

-Browser control, native user sign-in, gameplay, the launcher app, GoldHEN -AutoRun and recovery after rest mode are all confirmed on the development -console. Four controllers at a time; the console's own device limits apply. +

+ Latest release + Build + License: GPL v3 + Support on Ko-fi +

-See [CHANGELOG.md](CHANGELOG.md) for what is in a release, and -[SECURITY.md](SECURITY.md) for what being open on your network does and does not -mean. +**Use your phone or PC as a PS4 controller everywhere: on the home screen, at sign-in, and in every game.** -## Requirements +Control4Free adds up to four extra DualShock 4 controllers to a jailbroken PS4. Open its page in a browser on +your network, pick a controller, and choose who's playing on the PS4's own *"Who's using this controller?"* +screen. Play with touch, a keyboard, or the gamepad you already have: Xbox, DualSense, Switch Pro and most +others work through the phone or PC. There's nothing to install on the phone or computer. -- A PS4 running GoldHEN. Development console: firmware 10.01, GoldHEN 2.4b18.10. - Starting Control4Free from GoldHEN itself (LaunchPad or AutoRun) needs 2.4b18.10 - or later; the launcher app and PC sending need PayLoader. -- Nothing connected to GoldHEN's klog viewer while you add a controller. - Control4Free reads the kernel log itself to recover a new device's handle, and - the log has a single reader. -- A phone or PC on the same network. -- Turn off other GoldHEN controller plugins in games you play with Control4Free. A - plugin that takes over a signed-in user's controller can stop games from reading - the virtual controllers. +![The controller on a phone](docs/images/controller.png) -## Install +## Why it's different -1. Copy `build/Control4Free-.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 - starts it every time it loads. It also starts it right away if GoldHEN's - PayLoader is on; otherwise restart the PS4 and run the jailbreak again. -3. Scan the QR code or enter the address on your phone or PC. Select a controller - there and sign in through the PS4 screen. +Controller plugins live inside games, so they can't reach the home screen or the sign-in screen. Control4Free +is a GoldHEN payload that goes through Sony's own virtual-device system, the one Remote Play uses. The PS4 +sees a real controller, so it works wherever a DualShock does: -The app stays useful afterwards: it shows whether Control4Free is running, its -address and QR code, and how many controllers are connected. +- **on the home screen and in every menu**, PS button included; +- **at sign-in**, through the PS4's own user picker, for real users and guests alike; +- **in every game**, with no per-game setup. -- **Cross** starts Control4Free when it is not running (PayLoader must be on). -- **Triangle** turns GoldHEN's auto-start on or off, or updates it after you - install a newer package. -- **Square**, then **Cross**, stops Control4Free and disconnects its controllers. -- **Circle** closes the app. Control4Free keeps running without it. +## Features -### Without the app +- **Up to four controllers**, from as many phones, tablets or computers as you like. +- **A full DualShock 4 on the screen**: analog sticks, a two-finger touchpad, L3/R3, PS, Share and Options. + Move, resize or hide any button with the layout editor; each device keeps its own layout. +- **The gamepads you already have.** Connect one to the phone or PC and give it a controller. One computer + can run all four. +- **A keyboard**, with keys you can change. +- **Fast.** Input reaches the console the moment it arrives, at a DualShock 4's own rate while you play. +- **An app on the PS4** that shows the address and a QR code, how many controllers are connected, and starts + or stops Control4Free. +- **Starts by itself.** One button in the app sets up GoldHEN's AutoRun, so Control4Free is ready after + every restart. +- **On your phone's home screen**, like an app. +- **Stays awake**: your phone's screen doesn't turn off while you play. +- **Gets through rest mode.** After the PS4 wakes up, open the page and pick your controller again. -GoldHEN's own **Payloader LaunchPad** (under **Utilities**) does the same job: +## Quick start -1. Put `control4free.elf` in `/data/payloads/` on the PS4, for example through - GoldHEN's FTP server (port 2121). -2. In the LaunchPad, select `control4free.elf` to start it, or press **Square** on - it to add it to the AutoRun queue. The queue is `/data/GoldHEN/payloads.ini`: +1. Download `Control4Free-1.0.0.pkg` from the [latest release](https://github.com/MoHadiShibli/Control4Free/releases/latest). +2. Install it with GoldHEN's Package Installer: **Settings → Debug Settings → Game → Package Installer**, from a + USB drive or `/data/pkg/`. +3. Open **Control4Free** from **Library → Applications** and press **Cross**. It sets up auto-start and + starts Control4Free. +4. On your phone or PC, scan the QR code or open the address shown, for example `http://192.168.1.20:4264`. + Pick a controller, then choose your user on the TV with the D-pad and Cross. - ```ini - [AutoRun] - /user/data/payloads/control4free.elf = 1 - ``` +New to GoldHEN packages? The **[installation guide](docs/installation.md)** walks through every step. -When you update Control4Free this way, replace `/data/payloads/control4free.elf`. +## Screenshots -**Upgrading from the original test payload:** use **Stop Control4Free** in the -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. +| Home screen on a phone | On a PC | The app on the PS4 | +|---|---|---| +| ![Home screen on a phone](docs/images/home.png) | ![The page in a PC's browser](docs/images/desktop.png) | ![The Control4Free app on the PS4](docs/images/launcher.png) | -**Rest mode:** 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 -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. +## Documentation -**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. +- **[Installation](docs/installation.md)**: requirements, installing, auto-start, updating, uninstalling. +- **[Playing](docs/playing.md)**: signing in, touch, keyboard and gamepads, the layout editor, settings. +- **[Troubleshooting](docs/troubleshooting.md)**: the messages you might see and what to do about each. +- **[How it works](docs/how-it-works.md)**: the virtual-device API, sign-in, and what runs where. +- **[Protocol](docs/protocol.md)**: the WebSocket protocol between the page and the console. +- **[Security](SECURITY.md)**: what being open on your home network does and doesn't mean. +- **[Contributing](CONTRIBUTING.md)**: building, testing and code style. +- **[Changelog](CHANGELOG.md)**. -Diagnostics are saved at `/data/control4free/control4free.log`, with elapsed timestamps -such as `[c4f] [+00:01:23.456]`; the PS4's calendar setting is not used. Starting -a new instance preserves the last run in `control4free.log.previous`. A heartbeat every -minute and explicit network recovery messages help locate any remaining hang. +## Requirements and compatibility -## Use +- A PS4 with GoldHEN. Tested on firmware 10.01 with GoldHEN v2.4b18.10. Auto-start and starting Control4Free + from GoldHEN's menu need v2.4b18.10 or later. +- A phone, tablet or computer with a browser, on the same network as the PS4. +- Turn off other GoldHEN controller plugins in games you play with Control4Free. A plugin that takes over a + signed-in user's controller can stop games from reading the virtual ones. +- A Remote Play session (chiaki-ng, for example) works alongside Control4Free. -1. Stop any older Control4Free payload before loading another. They all use port - 4264. -2. Start Control4Free: from GoldHEN (above), from the launcher app, or by sending - `build/control4free.elf` from a PC to GoldHEN's PayLoader on port 9090 with a - payload sender. The payload serves its own page; no PC web server is needed. -3. Open `http://YOUR-PS4-IP:4264` in the phone or PC browser. Control4Free shows - the exact address on the TV when it starts. On a phone you can add the page to - the home screen (**Share → Add to Home Screen** on iOS, the browser menu on - Android) and skip typing it next time. -4. Select a free controller. This creates it and opens native PS4 user selection. - Use Left/Right and Cross to select a user or follow the PS4's guest flow. - **Do not press PS while the initial user-selection screen is open:** in the - hardware experiment this cancelled selection. PS works after sign-in. -5. Test the home screen and then a game before adding another controller. +## Limitations -For an Xbox or other physical controller, connect it to the phone/PC, press a -button so the browser detects it, then choose its virtual-controller slot under -connected controllers. Sign it in through the same PS4 screen. Add and sign in -controllers one at a time. Touch and keyboard can also operate the selected slot. +- **No rumble, light bar or motion yet.** The PS4 does send rumble and light-bar changes to the virtual + controllers; passing them to the phone is planned for 1.1. +- **One controller is added at a time.** If two people pick at once, the second is asked to try again a + moment later. +- **Rest mode signs everyone out**, so controllers have to be picked again after the PS4 wakes up, just like + real ones. +- **No pairing or password.** Anyone on your network can take a free controller. Read [SECURITY.md](SECURITY.md) + and keep port 4264 off the internet. -The corner menu provides local layout/keyboard preferences, **Disconnect -controller**, and **Stop Control4Free**. PS and Share send actual controller -buttons. The PS4 assigns users; there are no user settings on the page. +## Support -Some browsers restrict Gamepad API access on an HTTP page. If the page reports -that restriction, try opening a saved copy of `client/index.html` locally and -entering `YOUR-PS4-IP:4264` in its connection settings. Browser support for local -files varies. Keep the controller page in the foreground while playing. +Found a bug? [Open an issue](https://github.com/MoHadiShibli/Control4Free/issues/new/choose); I maintain this +project and read every report. If Control4Free is useful to you, you can help keep it going on +[Ko-fi](https://ko-fi.com/mohadishibli). -## Troubleshooting +## Credits -**"Signing a controller in needs the PS4's kernel log, and something else has -it."** A klog viewer is connected to GoldHEN. Close it and select the controller -again. Only controller sign-in needs the log, so this never interrupts play. +- [seregonwar/SplashDown](https://github.com/seregonwar/SplashDown): Control4Free's virtual-device code is + ported from its `psbutton.c`, the first working use of this API on a PS4. +- [GoldHEN](https://github.com/GoldHEN/GoldHEN), its PayLoader and AutoRun, and the + [GoldHEN Plugins SDK](https://github.com/GoldHEN/GoldHEN_Plugins_SDK). +- [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) by John Törnblom builds the payload, and the + [OpenOrbis PS4 Toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain) builds the app and its package. +- [Ghostcontrol](https://github.com/srbraboo/Ghostcontrol-PS5-USB-Controller-Patcher) was a research reference. +- [Apollo Save Tool](https://github.com/bucanero/apollo-ps4) showed the package settings that file an app under + Applications. +- [jsmn](https://github.com/zserge/jsmn), Project Nayuki's + [QR Code generator](https://github.com/nayuki/QR-Code-generator), + [stb_truetype](https://github.com/nothings/stb) and the [Roboto](https://github.com/googlefonts/roboto) font. +- The on-screen buttons are drawn after the + [DualShock 4 layout diagram](https://commons.wikimedia.org/wiki/File:Dualshock_4_Layout.svg) by Tokyoship + (CC BY 3.0). -**"Control4Free cannot add controllers until it is restarted."** The PS4 did not -report a new device, so Control4Free stops adding more rather than leave devices -behind that it can no longer address. Stop it in the app (Square, then Cross) and -start it again with Cross, or restart the PS4. +Full licence details are in [THIRD_PARTY.md](THIRD_PARTY.md). -**"Another controller is being connected; try again in a moment."** Controllers -are created one at a time. Wait a second and select yours again; everyone already -playing is unaffected. +### Built with AI help -**The page does not load.** Check the address on the TV, that the phone or PC is on -the same network, and that `http://` is used rather than `https://`. If the browser -corrects it to a search, type the address with `http://` in front. +Control4Free was built with the help of AI models, Claude by Anthropic and Codex by OpenAI, which worked on the +code, tests and documentation with me. AI-written code can look right and still be wrong. Reviews are very +welcome: if you spot AI slop (dead code, wrong assumptions, needless complexity), please +[open an issue](https://github.com/MoHadiShibli/Control4Free/issues) or send a pull request. -**The browser says it cannot use controllers on this page.** Some browsers only -allow the Gamepad API on secure pages. Touch and keyboard still work. For a -physical pad, save a copy of the controller page to the device, open the saved -file, and enter the console address in its connection box. +## License -**A game ignores the virtual controller.** Turn off other GoldHEN controller -plugins for that game. A plugin that takes over a signed-in user's controller can -stop games from reading Control4Free's. - -**Nothing happens after waking the PS4 from rest mode.** Rest mode signs everyone -out, so controllers have to be selected again. Reopen the page and pick yours. If -the page will not connect at all, start Control4Free again from the app. - -**The app says Control4Free is not responding.** Wait a few seconds after waking, -then press Cross again. If it stays stuck, restart the PS4 and run the jailbreak. - -The payload's own log is at `/data/control4free/control4free.log` on the console, -readable over GoldHEN's FTP server, with the previous run kept beside it. - -## Connection behavior - -- Opening the page does not create a controller. Creation follows an explicit - controller selection; the payload never presses sign-in buttons automatically. -- Each virtual slot has one browser owner. Multiple local inputs may share that - browser's selected slot, or use separate slots. -- A lost connection releases held buttons. After 3 seconds without updates, - inputs become neutral; after 15 seconds the controller is removed. A short - disconnect leaves a 15-second window to select that controller again. -- Explicit disconnect removes the controller immediately. Stopping the payload - removes all controllers and restores the saved host credentials. Browser Stop - refuses while another browser owns a controller. The launcher's confirmed Stop - can disconnect all controllers through its launcher API, which websites cannot use. -- If the kernel log is not available yet when Control4Free starts (AutoRun can - start it early), it connects to it when the first controller is created. -- There is no time limit. Rebooting the console ends it; AutoRun, the launcher or - a PC starts it again. - -## Build - -Everything builds in Docker, so nothing has to be installed on the machine. CI -(`.github/workflows/build.yml`) runs exactly these steps and attaches the payload, -the package and their SHA256 sums to each tagged release. - -The [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) toolchain is pinned -in the Docker image: - -```sh -docker build -t control4free-build docker/ -docker run --rm -v "$PWD:/src" -w /src control4free-build make -``` - -Output: `build/control4free.elf`, with the compressed page embedded. The version -comes from the `VERSION` file at the top of the repository. - -To build the installable launcher after the payload image is available: - -```sh -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' -``` - -The host test suites build the real sources with the PS4 calls stubbed, so they -need no console: - -```sh -docker run --rm --network none -v "$PWD:/src" -w /src control4free-launcher-build python3 -B tests/run.py -``` - -Output: `build/Control4Free-.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. - -## Current limits - -Rumble, lightbar feedback and motion input are not implemented. Multiple native -user sessions are unverified. User-assignment status comes from kernel-log events -and may lag or miss an event; check the TV. -Logs are written to `/data/control4free/control4free.log` on the console. - -## Credits and license - -The VDA code is ported from [seregonwar/SplashDown](https://github.com/seregonwar/SplashDown). -The build uses John Törnblom's [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) -and the [OpenOrbis PS4 Toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain). -[Ghostcontrol](https://github.com/srbraboo/Ghostcontrol-PS5-USB-Controller-Patcher) -was a research reference. JSON parsing uses [jsmn](https://github.com/zserge/jsmn); -the launcher uses [QR Code generator](https://github.com/nayuki/QR-Code-generator), -[stb_truetype](https://github.com/nothings/stb) and the -[Roboto](https://github.com/googlefonts/roboto) font. - -GPL-3.0; see [LICENSE](LICENSE) and [THIRD_PARTY.md](THIRD_PARTY.md). +Control4Free is free software under the [GNU General Public License v3](LICENSE). +Copyright © 2026 MoHadiShibli. diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md index e12e742..1690826 100644 --- a/THIRD_PARTY.md +++ b/THIRD_PARTY.md @@ -31,6 +31,13 @@ `Roboto-Light.ttf` `a08729d794801eaa158c53b1558f4cc351c3b1791eb3d22faf7058b2b7df9c8c`, `Roboto-Regular.ttf` `f3edb8058e523f5612bfd99d0745e661568ad85e1b6217bc62f786fabae624c6`. +- The on-screen DualShock 4 buttons in `client/index.html` are drawn after the + [DualShock 4 layout diagram](https://commons.wikimedia.org/wiki/File:Dualshock_4_Layout.svg) + by Tokyoship, licensed under CC BY 3.0 + (https://creativecommons.org/licenses/by/3.0/). +- The package settings that file the app under Applications (`CATEGORY`, + `ATTRIBUTE`) follow Apollo Save Tool (https://github.com/bucanero/apollo-ps4). + These license notices are also included inside the package. The HTTP/WebSocket transport in `src/net.c` is a new implementation. diff --git a/client/index.html b/client/index.html index f091b47..7fd6401 100644 --- a/client/index.html +++ b/client/index.html @@ -1192,10 +1192,10 @@ set(key, value) { try { localStorage.setItem('c4f.' + key, JSON.stringify(value)); } catch (e) { /* private mode */ } }, }; - // Settings of this device (the console's settings live on the console) + // Settings of this device. They stay in this browser. const DEVICE_SETTINGS = [ ['keepAwake', 'Keep the screen on', 'While this page drives a controller, the screen doesn\'t turn off.', true], - ['haptics', 'Vibration', 'Buzz this device when you press a button or the game rumbles (Android).', 'vibrate' in navigator], + ['haptics', 'Vibration', 'Buzz this device when you press a button (Android).', 'vibrate' in navigator], ['floating', 'Floating sticks', 'A stick starts wherever your thumb lands inside its area.', false], ['tapClick', 'Tap the touchpad to click', 'A quick tap presses the touchpad button. Hold still to keep it pressed.', true], ['stickClick', 'Double-tap a stick for L3 / R3', 'Tap a stick, then touch it again right away to press it in. Hold the second touch to keep it pressed. Off: L3 and R3 get their own buttons.', true], diff --git a/docs/how-it-works.md b/docs/how-it-works.md new file mode 100644 index 0000000..aa09640 --- /dev/null +++ b/docs/how-it-works.md @@ -0,0 +1,106 @@ +# How it works + +``` + phone / PC browser PS4 +┌──────────────────┐ WebSocket ┌───────────────────────────────────────────────┐ +│ controller page │ ────────────► │ Control4Free service (GoldHEN payload, :4264) │ +│ touch · keys · │ JSON, LAN │ ├─ scePadVirtualDevice* ─► virtual DS4 ×4 │ +│ gamepads │ ◄──────────── │ └─ /dev/klog (only while one signs in) │ +└──────────────────┘ status │ │ + │ Control4Free app (home screen) │ + │ └─ /api/status · /api/stop · AutoRun setup │ + └───────────────────────────────────────────────┘ +``` + +## Why a payload, not a plugin + +GoldHEN loads plugins into game processes only. The home screen and the sign-in screen belong to the system's +own processes, which never load plugins, so a plugin can't reach them. Control4Free is a payload instead, and +uses Sony's virtual-device API: the one Remote Play goes through. A device made with it is a real MBus device, +and the whole system sees it like a DualShock 4 plugged in over USB. + +## Where it runs + +GoldHEN's PayLoader doesn't start a process of its own: it runs the ELF inside an existing system process +(ScePartyDaemon on the development console). Control4Free: + +1. takes an exclusive lock on `/data/control4free/instance.lock`, so a second copy (AutoRun plus the app, say) + leaves without touching anything; +2. saves the host process's credentials, then raises them to the authority the virtual-device calls need. It + restores exactly what it saved before it exits, and refuses to change them at all if it couldn't save them; +3. loads `libSceMbus` with `dlopen` before the first pad call. `libScePad`'s imports from it are unresolved in a + payload, and calling into `libScePad` first can kill the process; +4. calls `scePadInit()`, then `scePadSetProcessPrivilege(1)`, in that order: the other way round the privilege + call fails with *not initialised*; +5. serves the page and the WebSocket on port 4264 until it's stopped. + +## Creating a controller + +`scePadVirtualDeviceAddDevice` returns a status, not a handle; on this console it returns `0x803b0006` even +when the device is created. The handle is the device's MBus *DeviceId*, and the only place a payload can learn +it is the kernel log, where the login manager announces every new device: + +``` +#LOGIN MGR# Receive Event : SCE_MBUS_EVENT_DEVICE_ADDED [DeviceId:0x7030d][type:1][subType:2] +``` + +So the service reads `/dev/klog` while it adds a device and accepts only that line, `subType:2` being the +Remote Play pad. Anything looser could hand it the DeviceId of a real controller plugged in at the same moment. +If no line arrives, it stops creating controllers until it's restarted, rather than leave devices behind that +it can no longer address. + +The device is created for user 1, so it arrives with no user, and the PS4 asks *"Who's using this +controller?"*. The service never presses anything itself: the choice is made at the TV. When a user is picked, +the login manager logs `DEVICE_OWNER_CHANGED [DeviceId:…][UserId:…]`, which is how the page learns the +controller is signed in. + +None of this blocks. Creating a controller is a small state machine the main loop advances: drain the log's +backlog, write a marker and wait for it to come back (proof that the reader really delivers), call +`AddDevice`, wait for the line. The other players keep playing and keep being answered meanwhile. + +## The kernel log + +`/dev/klog` has a single reader. GoldHEN's log server on port 3232 opens it only while it has a client, serves +one client at a time, and can go minutes without serving the next one after a client leaves. So the service +never uses the log server: it opens the device itself, only while a controller is signing in, and closes it +once every controller has a user, leaving the log to GoldHEN for the rest of the session. + +## Input + +The page sends the whole controller state as a compact array (see [protocol.md](protocol.md)). The service +turns it into a `ScePadData` sample and hands it to `scePadVirtualDeviceInsertData`: + +- as soon as it arrives, unless a report went out in the last 4 ms; +- every 4 ms while input changed in the last half second, as fast as a real DualShock 4 reports; +- every 16 ms otherwise, to keep the controller alive; +- each sample carries a rising timestamp and counter, as a real pad's do; +- a short queue keeps every button press and release, so a quick tap can't fall between two reports, while + stick movement only ever keeps the latest position. + +When a page stops sending, its buttons are released at once; after 3 seconds the controller goes neutral, and +after 15 it's removed. A page that reconnects within 15 seconds gets its controller back. + +## Rest mode and failures + +The main loop watches both the monotonic clock and the wall clock. A gap of more than 5 seconds means the +console slept: the service closes every connection, releases the kernel log, rebuilds its listening socket, +and carries on. The same happens on a network failure. Users are signed out by rest mode anyway, so pages pick +their controllers again. + +## The app + +The app on the home screen is an OpenOrbis homebrew application that draws its own screen in software, in the +same style as the page. Before it does anything else it leaves the application sandbox through GoldHEN's SDK +call (syscall 500, command 2), because the sandbox refuses connections to the console itself, including +`127.0.0.1`. Then it: + +- checks the service with `GET /api/status` every 2 seconds, and stops it with `POST /api/stop`; +- starts it by sending its own bundled copy of the payload to GoldHEN's PayLoader on port 9090; +- sets up auto-start by copying that payload to `/data/payloads/control4free.elf` and adding one line to the + `[AutoRun]` section of `/data/GoldHEN/payloads.ini`, written to a temporary file and renamed over the + original so a power cut can't leave it half written. + +## Security + +Being open on the home network is deliberate. What that does and doesn't protect is in +[SECURITY.md](../SECURITY.md). diff --git a/docs/images/controller.png b/docs/images/controller.png new file mode 100644 index 0000000..9841b9d Binary files /dev/null and b/docs/images/controller.png differ diff --git a/docs/images/desktop.png b/docs/images/desktop.png new file mode 100644 index 0000000..32d833d Binary files /dev/null and b/docs/images/desktop.png differ diff --git a/docs/images/home.png b/docs/images/home.png new file mode 100644 index 0000000..32aa641 Binary files /dev/null and b/docs/images/home.png differ diff --git a/docs/images/icon.png b/docs/images/icon.png new file mode 100644 index 0000000..c62219f Binary files /dev/null and b/docs/images/icon.png differ diff --git a/docs/images/launcher.png b/docs/images/launcher.png new file mode 100644 index 0000000..a77959c Binary files /dev/null and b/docs/images/launcher.png differ diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..b117a68 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,96 @@ +# Installation + +Control4Free comes in two pieces: + +- **The service** (`control4free.elf`), a GoldHEN payload that runs in the background, creates the virtual + controllers, and serves the controller page on port 4264. +- **The app** (`Control4Free-.pkg`), which goes on the PS4's home screen. It starts and stops the + service, shows the address and a QR code, and sets up auto-start. The package carries its own copy of the + service, so the package is all you need. + +## Requirements + +- A PS4 with [GoldHEN](https://github.com/GoldHEN/GoldHEN). Tested on firmware 10.01 with GoldHEN v2.4b18.10. + Auto-start and GoldHEN's Payloader LaunchPad need v2.4b18.10 or later. +- **Debug Settings** turned on in GoldHEN, for the Package Installer. +- A phone, tablet or computer with a modern browser, on the same network as the PS4. + +## Install the app + +1. Download `Control4Free-.pkg` from the + [latest release](https://github.com/MoHadiShibli/Control4Free/releases/latest). +2. Get it onto the PS4, either way: + - copy it to a USB drive formatted exFAT or FAT32 and plug it in; or + - turn on GoldHEN's FTP server and upload it to `/data/pkg/`. +3. On the PS4, open **Settings → Debug Settings → Game → Package Installer**, choose the USB drive or the + internal storage, and install **Control4Free**. + +The app appears in **Library → Applications**. + +## First start + +Open **Control4Free** and press **Cross**. The app: + +1. copies the service to `/data/payloads/control4free.elf`; +2. adds it to GoldHEN's AutoRun list, `/data/GoldHEN/payloads.ini`, so GoldHEN starts it every time it + loads; +3. starts it straight away if GoldHEN's **PayLoader** is on. If it isn't, restart the PS4 and run the + jailbreak: AutoRun starts Control4Free from then on. + +Once it's running, the app shows the address to open, for example `http://192.168.1.20:4264`, and a QR code +for your phone's camera. The PS4 also shows the address in a notification when the service starts. + +AutoRun keeps other entries in `payloads.ini` as they are. + +## The app's buttons + +| Button | What it does | +|---|---| +| **Cross** | Starts Control4Free when it isn't running. Until auto-start is on, it sets that up first. | +| **Triangle** | Turns auto-start on or off. After you install a newer package, it updates the auto-start copy. | +| **Square**, then **Cross** | Stops Control4Free and disconnects every controller. | +| **Circle** | Closes the app. Control4Free keeps running without it. | + +## Updating + +1. Install the new package over the old one. Delete the old app first if the installer refuses. +2. Open Control4Free. It says the auto-start copy is older than the one in the app: press **Triangle** to + update it. +3. The old version is still the one running. Press **Square**, then **Cross** to stop it, and **Cross** + again to start the new one (this needs GoldHEN's PayLoader). Or restart the PS4 and run the jailbreak. + +Your controller layouts and keys live in each phone's browser, so they survive updates. + +## Without the app + +The service is an ordinary GoldHEN payload, so the app is optional. + +**With GoldHEN's Payloader LaunchPad** (under **Utilities** in GoldHEN's menu): + +1. Put `control4free.elf` from the release in `/data/payloads/` on the PS4, for example over GoldHEN's FTP + server (port 2121). +2. In the LaunchPad, select `control4free.elf` to start it once, or press **Square** on it to add it to the + AutoRun queue. The queue lives in `/data/GoldHEN/payloads.ini`: + + ```ini + [AutoRun] + /user/data/payloads/control4free.elf = 1 + ``` + +**From a computer**: send `control4free.elf` to GoldHEN's PayLoader on port 9090 with any payload sender. +It runs until the PS4 restarts. + +## Uninstalling + +1. In the app, press **Triangle** to turn auto-start off, then **Square** and **Cross** to stop the service. +2. Delete the app from the Library. +3. Delete `/data/payloads/control4free.elf`, and `/data/control4free/` if you want the logs gone too. + +## Files on the console + +| Path | What it is | +|---|---| +| `/data/payloads/control4free.elf` | The copy of the service that AutoRun starts. | +| `/data/GoldHEN/payloads.ini` | GoldHEN's AutoRun list. The app adds or removes one line. | +| `/data/control4free/control4free.log` | The service's log, with the previous run in `control4free.log.previous`. | +| `/data/control4free/instance.lock` | Keeps a second copy from starting next to the first. | diff --git a/docs/playing.md b/docs/playing.md new file mode 100644 index 0000000..decb1ce --- /dev/null +++ b/docs/playing.md @@ -0,0 +1,114 @@ +# Playing + +## Connect a controller + +1. Make sure Control4Free is running: the app on the PS4 says **Running** and shows the address. +2. On a phone, tablet or computer on the same network, open that address, for example + `http://192.168.1.20:4264`, or scan the app's QR code. +3. Pick a free controller. The PS4 shows its *"Who's using this controller?"* screen. +4. Choose a user with the D-pad (**Left** and **Right**) and press **Cross**, or follow the guest option. + +Connect one controller at a time, and finish the user screen before the next person picks theirs. **Don't +press PS while that screen is up**: it cancels the choice. PS works normally once you're signed in. + +The home screen shows all four controllers live: + +| State | Meaning | +|---|---| +| **Free** | Nobody is using it. Pick it to play. | +| **Connecting** | The PS4 is setting it up. Takes a moment. | +| **Choose user on TV** | Waiting for someone to pick a user on the PS4's screen. | +| **Connected** | Signed in and ready. | +| **Paused** | Its phone stopped sending input, for example because the screen locked. | +| **This device** / **+1 device** | Who's driving it: this browser, or other ones. | + +## Touch controls + +The controller screen is a DualShock 4 laid out for your screen, best held sideways: + +- **Sticks** follow your thumb. With **Floating sticks** on, a stick starts wherever your thumb lands. +- **The touchpad** takes one or two fingers. A quick tap clicks it; hold still to keep it pressed. +- **L3 and R3**: tap a stick, then touch it again straight away to press it in. If you'd rather have separate + buttons, turn off **Double-tap a stick for L3 / R3**. +- **PS, Share and Options** are real controller buttons, so PS takes you to the PS4's home screen. + +## Your own layout + +Open the menu (**☰**) and choose **Edit the button layout**. Drag a button to move it, change its size, or +hide it. **Reset to default** puts everything back; tap it twice, so a stray tap can't throw a layout away. Each device keeps its own layout, separately for portrait +and landscape. + +## Keyboard + +On a computer the keyboard drives the controller open on the page. Change any key in **All settings → +Keyboard**, and turn on **Show keyboard keys** to see them on the buttons. + +| Controller | Key | Controller | Key | +|---|---|---|---| +| Cross | Space | L1 / R1 | Q / E | +| Circle | Esc | L2 / R2 | 1 / 3 | +| Square | Backspace | L3 / R3 | Z / C | +| Triangle | Enter | Options | O | +| D-pad | I J K L | Share | V | +| Left stick | W A S D | PS | P | +| Right stick | Arrow keys | Touchpad click | U | +| Touchpad corners | T Y G H | | | + +## Gamepads + +Connect an Xbox, DualSense, Switch Pro or other gamepad to the phone or computer and press a button on it. It +appears under **Gamepads on this device**: + +- a gamepad that turns up while a controller is open on the page drives that controller; +- otherwise, pick the controller each gamepad plays as. One computer can run all four controllers this way; +- a row lights up while you use its gamepad, so you can tell which is which. + +Keep the page's window in front: browsers only read gamepads for the active window. + +**"This browser only allows gamepads on secure pages."** Some browsers only offer gamepads on `https` +pages, and the console's page is plain `http`. Save the page to the device (**Save page as** on a computer), +open the saved file, and enter the console's address in its connection box. Touch and the keyboard work either +way. + +## Settings + +**☰ → All settings** has this device's settings. They stay in this browser. + +| Setting | What it does | +|---|---| +| **Keep the screen on** | The phone's screen doesn't turn off while the page drives a controller. | +| **Vibration** | The phone buzzes briefly when you press a button (Android). | +| **Floating sticks** | A stick starts wherever your thumb lands in its area. | +| **Tap the touchpad to click** | A quick tap presses the touchpad button. | +| **Double-tap a stick for L3 / R3** | Off: L3 and R3 get their own buttons. | +| **Show keyboard keys** | Labels the buttons with their keyboard keys. | + +The bottom of the home screen shows the version, and the round trip to the console in milliseconds: a quick +way to see whether your Wi-Fi is the slow part. + +## On your phone's home screen + +Add the page to your home screen and it opens like an app: + +- **iPhone and iPad (Safari)**: **Share → Add to Home Screen**. It opens without Safari's bars. +- **Android (Chrome)**: **⋮ → Add to Home screen**. Because the page is plain `http`, it opens in a Chrome + tab. + +The icon only works while Control4Free is running, and it remembers the PS4's address: reserve that address +for the PS4 in your router so it doesn't change. + +**Full screen**: the **⛶** button in the corner. Safari on iPhone doesn't let pages go full screen, so there +it explains how to use the home screen instead. + +## Leaving + +- **☰ → Disconnect controller** removes it from the PS4 straight away. +- **Closing the page or locking the phone** releases its buttons at once. After 3 seconds without input the + controller goes idle, and after 15 seconds it's removed. Come back within 15 seconds and you keep it. +- **☰ → Stop Control4Free** stops the service and disconnects everyone. It's refused while someone else is + still playing; the app on the PS4 can always stop it. + +## Rest mode + +Rest mode signs every user out, so controllers have to be picked again after the PS4 wakes up, as real ones +do. Control4Free itself carries on: reopen the page and pick your controller. diff --git a/docs/protocol.md b/docs/protocol.md new file mode 100644 index 0000000..382bb26 --- /dev/null +++ b/docs/protocol.md @@ -0,0 +1,148 @@ +# Protocol + +The service listens on TCP port 4264 and speaks plain HTTP/1.1 and WebSocket (RFC 6455). This page is for +anyone writing their own client or checking what the page does. + +## HTTP + +| Request | Answer | +|---|---| +| `GET /`, `GET /index.html`, `GET /?…` | The controller page, gzip-compressed, with a Content-Security-Policy. | +| `GET /manifest.webmanifest` | The web app manifest, for a home-screen shortcut. | +| `GET /icon-192.png` | The app icon. | +| `GET /ws` with `Upgrade: websocket` | The control socket, below. | +| `GET /api/status`, `POST /api/stop` | The launcher API, below. | +| Anything else | `404`. | + +Every request needs a `Host` header that is a literal IPv4 address or `localhost`, with or without a port; +anything else gets `403`. This keeps out domain names that resolve to the console (DNS rebinding). + +## The control socket + +`GET /ws` upgrades to a WebSocket. The handshake is refused with `403` when it carries an `Origin` other than +`http://` or `null`; no `Origin` at all is accepted. Messages are UTF-8 JSON text frames of at +most 4096 bytes; a longer frame closes the connection. Fragmented messages and ping/pong control frames are +handled. + +### Requests + +```json +{"id": 7, "method": "claim", "params": [0]} +``` + +- `method`: a string. +- `params`: an array of up to 16 integers; required, even if empty. +- `id`: optional, an integer from 0 to 2^53−1. Requests with an `id` get a reply carrying the same `id`. +- `jsonrpc: "2.0"` is allowed and ignored. Any other key, or anything that isn't this shape, is answered with + an `Invalid request` error. + +Controllers are numbered 0 to 3 in the protocol and 1 to 4 on screen. + +| Method | Params | Reply | +|---|---|---| +| `info` | `[]` | `{"version": "1.0.0", "protocol": 2, "pads": 4}` | +| `status` | `[]` | A status object, below. | +| `claim` | the controllers this connection wants, for example `[0]` or `[0, 2]` | A status object, once every controller in the claim exists. | +| `u` | input, below | None. | +| `leave` | `[pad]` | A status object. | +| `stop` | `[]` | None; the service stops and the connection closes. | +| `ping` | `[]` | `{}` | + +**`claim`** is the whole set this connection wants: controllers missing from it that it owned are removed, +and free ones in it are created. A claim that needs a new controller is answered when the controller exists, +usually within a second; the service keeps serving every other connection in the meantime. It's refused, +changing nothing, if any controller in it belongs to another connection (`409`), if another controller is +being created at that moment (`409`), or if controllers can't be created (`503`). + +**`u`** sends the complete state of one controller this connection owns. Input for any other controller is +ignored: input never claims a controller. + +``` +[pad, buttons, lx, ly, rx, ry, l2, r2, fingers, (id, x, y) × fingers] +``` + +| Field | Range | +|---|---| +| `buttons` | a bit mask, below | +| `lx`, `ly`, `rx`, `ry` | 0 to 255, 128 is centred; up and left are 0 | +| `l2`, `r2` | 0 to 255 | +| `fingers` | 0 to 2 touches on the touchpad | +| touch `id` | 0 to 127, stable while the finger stays down | +| touch `x`, `y` | 0 to 1919, 0 to 941 | + +| Button | Bit | Button | Bit | +|---|---|---|---| +| Share | `0x0001` | L2 | `0x0100` | +| L3 | `0x0002` | R2 | `0x0200` | +| R3 | `0x0004` | L1 | `0x0400` | +| Options | `0x0008` | R1 | `0x0800` | +| D-pad up | `0x0010` | Triangle | `0x1000` | +| D-pad right | `0x0020` | Circle | `0x2000` | +| D-pad down | `0x0040` | Cross | `0x4000` | +| D-pad left | `0x0080` | Square | `0x8000` | +| PS | `0x10000` | Touchpad click | `0x100000` | + +Send the state whenever it changes, and at least once a second while the controller is in use. After 3 +seconds without input the controller goes neutral and shows as `paused`; after 15 it's removed. When a +connection closes, its controllers' buttons are released at once, and they're removed 15 seconds later unless +a connection claims them again; the page does so when it reconnects. + +**`stop`** is refused with `409` while another connection owns a controller. + +### The status object + +```json +{ + "version": "1.0.0", + "protocol": 2, + "pads": [ + {"pad": 0, "name": "Controller 1", "enabled": true, "open": true, "connected": true, "clients": 1, + "mine": true, "state": "ready", "uid": "1a2b3c4d", "color": [32, 96, 255], "reports": 5120, "error": 0} + ] +} +``` + +| Field | Meaning | +|---|---| +| `open` | The virtual controller exists. | +| `connected` | A connection owns it and is sending input. | +| `clients` | 1 if a connection owns it, else 0. | +| `mine` | This connection owns it. | +| `state` | `free`, `connecting`, `select` (waiting for a user on the TV), `ready` (signed in), `paused` (no input lately, or nobody connected), or `error` (the console refused input). | +| `uid` | The signed-in user's ID in hex, or `unassigned-…` before sign-in. | +| `color` | The controller's colour on the page. | +| `reports` | Samples given to the console so far. | +| `error` | The console's error code for the last refused sample, or 0. | + +### Messages from the service + +- `{"method": "s", "params": }`: the status, whenever something changes and at least once a second. +- `{"method": "error", "params": {"message": "…"}}`: a problem that isn't the reply to a request: a controller + removed for sitting unused, a request that couldn't be parsed, or an error for a request sent without an + `id`. + +Errors in reply to a request: + +```json +{"id": 7, "error": {"code": 409, "message": "Controller is in use on another device"}} +``` + +| Code | Meaning | +|---|---| +| `400` | The request or its values are invalid. | +| `404` | Unknown method: the page is newer than the service. | +| `409` | Someone else owns it, another controller is being created, or it isn't yours to `leave`. | +| `503` | Controllers can't be created now, the kernel log is busy, or the service is stopping. | + +## The launcher API + +For the app on the PS4. Both requests need the header `X-Control4Free-Launcher: 1`, and are refused with `403` +if they carry an `Origin`, an `Upgrade`, a body or `Transfer-Encoding`. A browser only sends a custom header +cross-origin after a CORS preflight, which the service never answers, so websites can't use these. + +``` +GET /api/status → {"application": "Control4Free", "api": 1, "version": "1.0.0", "controllers": 2, "stopping": false} +POST /api/stop → {"application": "Control4Free", "stopping": true} +``` + +`/api/stop` removes every controller, even ones in use, and the service exits a quarter of a second later. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..31c5614 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,113 @@ +# Troubleshooting + +Find the message you see, or the symptom, below. If nothing here helps, +[open an issue](https://github.com/MoHadiShibli/Control4Free/issues/new/choose) with the log described at the +end of this page. + +## On the controller page + +**"Signing a controller in needs the PS4's kernel log, and something else has it."** +A klog viewer is connected to GoldHEN's log server (port 3232). Close it and pick the controller again. Only +signing a controller in needs the log, so this never interrupts play. + +**"Another controller is being connected; try again in a moment."** +Controllers are added one at a time. Wait a second and pick yours again. Everyone already playing carries on +undisturbed. + +**"Control4Free cannot add controllers until it is restarted."** or +**"The PS4 did not report the new controller."** +The PS4 didn't confirm a new controller, so one may exist that Control4Free can't address. Rather than leave +more of those behind, it stops adding controllers. Stop it in the app (**Square**, then **Cross**) and start +it again with **Cross**, or restart the PS4. + +**"Controller is in use on another device."** +Someone else has that controller. Pick a free one. If it's yours from a phone that has gone, it frees itself +15 seconds after that phone stopped sending input. + +**"That controller was disconnected after sitting unused."** +Nothing drove it for 15 seconds, for example because the phone's screen locked. Pick it again. To keep the +phone awake while you play, leave **Keep the screen on** turned on in the settings. + +**"Someone else is still using a controller."** +The page's **Stop Control4Free** won't cut other players off. Ask them to disconnect, or stop it from the app +on the PS4. + +**"Control4Free on the PS4 is older than this page."** +The page is newer than the service it's talking to, typically a saved copy of the page. Update the PS4 side: +see [Updating](installation.md#updating). + +## The page doesn't open + +- Use the address the app or the notification shows, including `:4264`. +- Type `http://`, not `https://`. If the browser turns the address into a search, put `http://` in front. +- The phone or computer must be on the same network as the PS4: guest Wi-Fi networks often keep devices + apart. +- Check the app on the PS4 says **Running**. If it says **Not running**, press **Cross**. +- If the address worked before and doesn't now, the router may have given the PS4 a new one. Reserving an + address for the PS4 in the router's settings keeps it the same. + +## Picking a user + +**The *"Who's using this controller?"* screen doesn't appear.** Wait a few seconds after picking the +controller. If it still doesn't, disconnect the controller from the page's menu and pick it again. + +**The screen went away without a user.** PS cancels it. Pick the controller again and use only the D-pad and +Cross until you're signed in. + +**The page keeps saying "Choose user on TV" after signing in.** The page learns about sign-ins from the PS4's +kernel log. If a klog viewer was connected at that moment, it missed it. Play on: the controller works either +way. + +## In games + +**A game ignores the virtual controller.** Turn off other GoldHEN controller plugins for that game. A plugin +that takes over a signed-in user's controller can stop games from reading Control4Free's. + +**Buttons feel slow.** The number next to the version at the bottom of the home screen is the round trip to +the console. Above about 30 ms, your Wi-Fi is the slow part: move closer to the router or use 5 GHz. + +**No vibration from the game.** Rumble isn't passed to the phone or gamepad yet. It's planned for the next +version. + +## Gamepads + +**"This browser only allows gamepads on secure pages."** Save the page to the device, open the saved file, and +enter the console's address in its connection box. See [Gamepads](playing.md#gamepads). + +**The buttons are mixed up.** The page says *"buttons may be mixed up"* when the browser doesn't recognise the +gamepad's layout. Try another browser, or connect the gamepad another way (cable instead of Bluetooth, or the +other way round). + +**The gamepad does nothing.** Keep the page's window in front, press a button on the gamepad after the page +has loaded, and check it has a controller under **Gamepads on this device**. + +## In the app on the PS4 + +**"PayLoader did not answer."** Turn on GoldHEN's PayLoader and press **Cross** again, or restart the PS4 and +run the jailbreak: with auto-start on, Control4Free starts by itself. + +**"Control4Free is not responding."** Wait a few seconds after the PS4 wakes from rest mode, then press +**Cross** again. If it stays stuck, restart the PS4 and run the jailbreak. + +**"Sent, but Control4Free never answered."** or **"The transfer to PayLoader broke off."** Restart the PS4 +before trying again. The app won't send a second copy until then, so two can't end up running. + +**"Something answers on port 4264 but not the way this app expects."** or **"No answer the app +understands."** Something else is answering on port 4264, often an older version of Control4Free. Stop it +from its own page, or restart the PS4. + +**"The app cannot reach Control4Free."** The app couldn't connect to the service from its own sandbox. Close +the app and open it again; if that doesn't help, restart the PS4 and run the jailbreak. + +**"The bundled payload is damaged."** Reinstall the package. + +**Control4Free is listed under Games instead of Applications.** That's a package from before 1.0.0. Delete the +app and install the current package. + +## The log + +The service writes a log to `/data/control4free/control4free.log` on the PS4, and keeps the previous run in +`control4free.log.previous`. Turn on GoldHEN's FTP server and download them from port 2121, or connect to +GoldHEN's log server on port 3232 and look for lines starting with `[c4f]`. + +The log contains your console's network address. Remove it before posting the log if you prefer.