GhosttoothAPI - Custom payload + framework for bluetooth audio and bluetooth gamepads

TheStonedModder

Developer
Developer
Joined
Dec 25, 2022
Messages
3,014
Reaction score
5,198
Trophies
2
Age
29
XP
9,809
Country
United States
Only audio is supported for now. Meaning you can use any bluetooth audio device. Such as Airpods or Beats!

ghost-toothAPI



The PS5 has no Bluetooth audio. Sony's own wireless headsets need a USB dongle. The Bluetooth chip in the console is for DualSense, not headphones.

ghost-toothAPI is a payload that plays the console's sound — game and system UI on a normal Bluetooth headset, Turn the headset off to stop.

This is v1. Same Bluetooth path is meant to take third-party controllers later, plus a PKG with an on-console pairing screen. That is not in this build.




What you need



  • Jailbroken PS5 with a payload loader (the usual ELF send on 9021)
  • A Bluetooth headset that does A2DP (basically all of them)
  • DualSense can stay on. That is the point.

Only tested on 5.50.





How to use it



First time

  1. Put the headset in pairing mode.
  2. Send ghost-toothAPI.elf.
  3. Wait for the notification searching - put the headset in pairing mode.
  4. It scans ~10 s, ignores TVs/soundbars, and picks headphones (name + class). Notification shows the name it is connecting to.
  5. When you see audio on the headset - switch it off to stop, you are in.

Pairing is saved. Next send reconnects that headset. TVs are never saved.

Optional: /data/ghost-toothAPI/headset.ini — leave it blank, or name=beats / address=AA:BB:CC:DD:EE:FF (address as printed in the log).

Stop

Turn the headset off. That is the exit.

Stuck on "already linked"

Power-cycle the headset, send the ELF again. A killed payload can leave the radio thinking the headset is still connected. Do not send two copies at once.

Notifications are prefixed ghost-toothAPI:. If something fails, it tells you to read the log.





What you get / what you don't



WorksDoes not
Game audio + system sounds (same mix Remote Play uses)Mic / party chat from the headset
PS menu stays audible (not muted like the capture UI mix)Headset volume / pause buttons (no AVRCP)
DualSense stays wirelessaptX / AAC / LDAC — SBC only
No kernel patch, no install3D audio beds — you get the stereo mix Remote Play already has

SBC at 48 kHz if the headset allows it, otherwise 44.1. Bitpool 40, locked (not a 2–53 range — that made Beats Solo 3 sound like static).

If you close a game, the stream keeps going as silence and picks the mix back up when the next one starts. If audio drifts more than ~160 ms behind the picture, one packet is dropped.




If it breaks



Log is here:

Code:
/data/ghost-toothAPI/ghost-toothAPI.log

Bonded headset key:

Code:
/data/ghost-toothAPI/bond.key

Delete bond.key if you want to pair a different headset (or if auth keeps failing — a bad key is deleted automatically, but you can do it by hand).

NotificationMeaning
already runningELF is already up. Wait, or the last run crashed less than 15 seconds ago.
audio capture unavailableCould not open the Remote Play capture path. Log has the error.
the Bluetooth controller did not answerUSB HCI setup failed. Log has the error.
no headphones found - pairing mode?Scan only saw TVs/speakers. Headset needs to stay in pairing until it connects.
could not connect to the headsetPair / auth / A2DP failed. Headset off, out of range, or already on a phone.
turn the headset off and on, then try againRadio still has an old link. Power-cycle the headset.




How it actually works



Short version: the PS5 wireless chip is two Bluetooth controllers on one USB device.

  • Controller 1 is DualSense. We never touch it.
  • Controller 0 is sitting there claimed by the same kernel driver. We open /dev/ugen0.2 and talk HCI on those pipes without detaching the driver. Detach any interface on that chip and DualSense dies.

Because the kernel is still reading the same endpoints, some HCI events get stolen (ours, and some of the system's). The stack retries, ignores TVs, and keeps at most 3 ACL packets in flight on a controller that reports 7 buffers. Two copies of the payload must not run — they would split the same controller.

Audio is not a tap of the HDMI encoder. It is libSceAvcap2 opened the way Remote Play opens it (source 2, raw 48 kHz stereo float). That mix includes system sounds and is not muted when the PS menu is up. libSceIpmi has to be loaded first or the process dies with no signal.

A2DP is Source, SBC, no AVRCP, no HFP. Headset sees the name ghost-toothAPI.

USB node: /dev/ugen0.2

Code:
Function 0 (this payload)
  events 0x82, ACL in 0x81, ACL out 0x01
  commands: control pipe, class-device, bRequest 0

Function 1 (system DualSense)
  events 0x85, ACL in 0x84, ACL out 0x04

Do not HCI Reset. Do not unbind interfaces.

Capture NIDs (resolved with kernel_dynlib_resolve, not dlsym by name):

Code:
svzPXluOz8U  sceAvcap2Initialize
gojqghDU+1Y  sceAvcap2Terminate
3nHpE7Dp5SE  sceAvcap2OpenAudio
tR7gPe1i8hw  sceAvcap2Close
8CGNCwBsItI  sceAvcap2Start
Z5dKK0xnQ8g  sceAvcap2Stop
WhsHggqdwcg  sceAvcap2ReadAudio

OpenAudio block is 0x38 bytes, source=2, mode=0. ReadAudio 0x81950002 empty, 0x81950004 overrun, 0x81950008 session ended.

State:

Code:
/data/ghost-toothAPI/ghost-toothAPI.log
/data/ghost-toothAPI/ghost-toothAPI.lock
/data/ghost-toothAPI/bond.key          (6-byte addr + 16-byte key + 1-byte type)
/data/ghost-toothAPI/headset.ini       (optional name= / address=)

Build: ps5-payload-dev SDK, make → ghost-toothAPI.elf.




What's next



  • HID — third-party controllers on the same controller-0 path
  • Companion PKG — pairing UI on the console, device list, not just notifications




Bug reports: paste the last page of /data/ghost-toothAPI/ghost-toothAPI.log, headset model, and whether DualSense dropped. If DualSense dropped, say so first — that means something unbound USB, which this payload is not supposed to do.
 
  • Like
Reactions: br0ke23 and ViRGE

Site & Scene News