Files
Dean 651e248bef Merge pull request #11845 from Ralizaki/feat/libScePad-controller-settings
feat(libScePad): implement vibration and light bar brightness controls
2026-10-29 05:05:32 +13:00

16 KiB

libScePad technical debt

Silent stubs

Unknown function info

  • libScePad profile and device-setting exports - profile getters, tuning, saved settings, USB state, shared output and virtual-device setting arguments are recovered. Hunt and Leonardo reset each consume only a handle; physical VR USB status consumes only an output pointer. Remote settings output is 64 bytes and license-controller information is 1224 bytes. Kernel ioctl payload fields remain incomplete.

  • libScePad report and tracking exports - read, extended-read, tracker, UI, VR-controller and report argument registers are recovered. Feature reports use a byte report ID and 64-bit byte count, capped at 64. VR trigger effects consume a 56-byte record; the dual-controller call uses two handles and a mask with records at offsets 8 and 64. Remaining payload field layouts use incomplete declarations, including extended samples and kernel-owned extension reports.

  • scePadUnknown_n3kSX62fgNo (libScePad, NID n3kSX62fgNo) - the name is unknown, and the 0 returned after scePadInit is inferred, not measured. In the OS 10.01 system module the function takes no arguments, returns 0x80920005 before scePadInit, and otherwise sends a per-process value of 1 to ShellCore over Mbus, returning 0x809200ff if that call fails and 0 if it succeeds, with or without an open handle. The #3177 probe measured 0x809200ff in a process that is not a game; a game process is assumed to get 0. The ShellCore setting it changes is unknown and not modelled

  • scePadSetAngularVelocityDeadbandState (libScePad) - with the deadband on, each angular velocity axis whose magnitude is below 0.017453292 rad/s (1 degree per second) reads as 0, before the orientation is integrated; it starts off. Threshold, per-axis test and default follow duaLib, which reimplements the PC libScePad.dll; the console's threshold is not measured, and the deadband is kept per port slot. One title (PPSA04354) disassembled for this entry calls it once (0xead26b), only when a change bit of its own is set, with a 32-bit value read through a pointer and 0 or 1 taken from the 0x4 bit of a flags value, and ignores the result. The call does not show the threshold or the default.

  • PadQueueLegacyAudioHaptics, PadClearLegacyAudioHaptics (libScePad) - PadQueueLegacyAudioHaptics and PadClearLegacyAudioHaptics route legacy audio haptics only to port 0; multi-port queueing is not modelled

  • scePadGetControllerInformation (libScePad) - scePadGetControllerInformation always reports connectedCount = 1 and connected = true; behaviour across multiple open controller handles is not measured on hardware

  • scePadGetExtControllerInformation (libScePad) - on a standard-port handle it returns NOT_PERMITTED (0x80920101) and leaves the buffer as it is. Halo CE Universal (PPSA99500) measured that answer on firmware 13.59 for two connected DualSenses opened by the title, in three runs, with its probe zeroing the buffer first. The probe logged only bytes 28 to 47, which stayed zero, so whether the console writes any of the buffer is not known. The special port (type 2) still answers 0 with 0x2c zeroed bytes, which is not measured, and so are the order of the handle and null checks and the answer on a console that grants the permission.

  • scePadOpen and scePadOpenExt (libScePad) - the special port (type 2) opens a handle per user (PAD_SPECIAL_HANDLE + slot, 16 slots for the console's 16 accounts, a 17th user gets the unknown-user code 0x809b0081) that every pad call accepts: reads give the rest record (sticks 0x80, orientation w 1, acceleration y 1, connected false), information is zeroed, extended information has device class -1, setters succeed without effect. Not measured: the console's own handle values (fresh per open), a PS5, and the calls the PS4 Pro survey (firmware 10.71, no special controller) did not cover; it measured open, ALREADY_OPENED, scePadGetHandle, both reads, both informations, the five setters above, close, and that two logged-in users get independent handles. scePadOpenExt returns DEVICE_NOT_CONNECTED (0x80920007) for special ports, unmeasured; PPSA01317 skips a user on any negative result. scePadOpenExt throws for other port types

  • scePadOpen, scePadGetHandle, scePadRead and scePadReadState for the remote control port (libScePad) - whether a console without a media remote opens the system user's remote control port (type 16), returns DEVICE_NOT_CONNECTED or fills other fields is not measured. The host has no remote, so the port opens its own handle (PAD_REMOTE_HANDLE) that reads one zeroed entry with no buttons and connected false; the other pad calls reject it with INVALID_HANDLE. SDL's PS5 video driver maps this port's buttons to keys, so a remote that read the DualSense turned Cross into Return (Raptor PPSA94002).

  • scePadOpen (libScePad) - port types 3 and 4 throw: a payload process on a PS5 (firmware 13.60) let them past the type check, but it cannot open a port for its own logged-in user (0x809b0081), so what they open is unknown. The argument checks it did reach are followed: types 1, 5, 6, 255 and -1, and 16 for a user other than the system user, fail with INVALID_ARG before the user is looked at, user -1 with 0x809b0001 on types 0, 2, 3 and 4 and on index 1, and index 1 of a logged-in user with 0x809b0003; whether index 1 of types 3 and 4 fails the same way was not measured; the payload never tried type 16 with the system user (255), which opens the remote control port as below

  • scePadVrControllerReadState - the 104-byte output layout is recovered, including buttons, stick, triggers, touch fields, motion, connection state and timestamp. Touch-value semantics and kernel report production remain incomplete; the export still throws.

  • scePadVrControllerReadCombinedState - the two handle arguments and 128-byte output are recovered; output field semantics remain incomplete.

  • scePadSetVibrationMode (libScePad) - modes 1 to 3 and 5 are accepted and stored. On a PS5 (firmware 13.60) the call returns 0x80920005 before scePadInit, then rejects a mode of 0 or below with INVALID_ARG before it checks the handle, and returns INVALID_HANDLE for a bad handle with any higher mode; whether an open handle accepts modes above 3 was not measured, since the payload could not open a pad; 5 is accepted because PPSA25286 passes it on its open handle at start-up; the meanings of modes 3 and 5 are unknown (PPSA21480 calls it with 3 right after scePadOpen and aborts on failure; SharpProspero names only 1 and 2). The stored mode has no other effect

  • scePadIsRemoteController (libScePad) - the name hashes to NID fCWdlnmB1Ks; the signature (int handle, bool* remote) comes from PPSA16064, which zeroes a byte, passes its address after scePadGetControllerInformation and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false

  • scePadIsDS4Connected, scePadIsBlasterConnected and scePadIsMoveConnected (libScePad) - the bool (int handle) signatures are recovered; the library reports one DualSense to every title (see Touchpad resolution below), so no DualShock 4, Blaster or Move controller is ever behind a handle and each call answers false for any handle. The console's answer for an invalid handle is not measured

  • scePadIsValidHandle (libScePad) - the bool (int handle, int portType) signature is recovered. Firmware 13.42's module, as BunnyFl ran it natively over a slot table he wrote (comment; the driver emulated, the table not built by scePadOpen), answers 1 only when a slot holds the handle with exactly that port type, and 0 while not initialised. Here it is the same match on each open handle's type: 0 for a standard handle while its port is open, 2 for a special handle while its user's slot is open, 16 for the remote handle while the system user's remote port is open, and no match before scePadInit. scePadOpen opens no type-1 handle here, so no handle matches type 1. A closed standard handle is not valid, although the light bar, vibration and other setters keep accepting it

  • scePadResetLightBarAll and scePadResetLightBarAllByPortType (libScePad) - the (void) and (uint32_t portType) signatures are recovered, and both follow firmware 13.42's module as BunnyFl ran it natively (comment; the driver emulated, port type 1 unknown, the table not built by scePadOpen). By port type, any type but 0, 1 and 2 returns INVALID_ARG before the initialisation check (3, 4, 16 and -1 measured). Otherwise each open handle of that type is reset as scePadResetLightBar resets it, and the call returns PAD_OK, also before scePadInit, when it does nothing; scePadResetLightBarAll is type 0. In this model a special handle's reset has no effect, as special handles have no light bar, and scePadOpen opens no type-1 handle

  • scePadSetVibrationForce (libScePad) - the (int handle, const PadVibrationParam*) signature is recovered. Firmware 13.42's module, as BunnyFl ran it natively (comment; the driver emulated), returns 0x80920005 before scePadInit, even for a null param, then INVALID_ARG for a null param; otherwise it sends scePadSetVibration's request with a force byte set, without looking the handle up, and returns the driver's answer. There is no pad driver here: a special handle returns PAD_OK without effect, as with scePadSetVibration, and a standard handle sets its pad port's motors whether or not it is still open. A handle with no pad port throws, as the driver's answer for it is not measured. In this model the force byte has no effect, as there is no system vibration-off setting to override (scePadSetVibrationMode only stores its mode)

  • scePadSetAngularVelocityBiasCorrectionState (libScePad) - inferred signature (int handle, bool enabled) follows scePadSetTiltCorrectionState and scePadSetAngularVelocityDeadbandState; an invalid handle returns PAD_ERROR_INVALID_HANDLE, disabling is a no-op, and enabling throws because bias correction is not modelled

  • scePadGetAccessibilityControllerState (libScePad) - the value a console reports for a DualSense is not measured; the emulated pad, which is not an Access controller, reports 0. The name hashes to NID zBsgF0q8DIM. In the OS 10.01 system module the (int handle, uint32_t* state) function returns 0x80920005 before scePadInit, then INVALID_ARG for a NULL state and INVALID_HANDLE for a bad handle, and otherwise asks the pad driver for a per-device value of the device behind the handle: 0 when no device is behind it, 1 when the driver has no answer, else the driver's value. What the values mean is unknown

  • scePadGetControllerType (libScePad) - the value a console reports for a DualSense is not measured; the emulated pad reports PAD_CONTROLLER_TYPE_DUAL_SENSE (2), following duaLib's s_SceControllerType enum where UNKNOWN = 0 and DUALSENSE = 2; special handles report 0 (UNKNOWN). The signature (int handle, int32_t* type) follows duaLib, while the validation order (checking initialization, null pointer, then handle) follows the library's other pad queries; duaLib does not validate null pointers and returns PAD_ERROR_DEVICE_NOT_CONNECTED for an invalid pad.

  • scePadDisableVibration, scePadIsLightBarBaseBrightnessControllable (libScePad) - scePadDisableVibration(true) stops active running motors on the port without disabling vibration for subsequent scePadSetVibration calls; scePadIsLightBarBaseBrightnessControllable reports 1 for standard DualSense handles and 0 for special handles while the modifier is a no-op.

  • libScePad internal data and device control - kernel-owned version, telemetry, Lotus-button and update payload fields remain unresolved. Lotus-button parameter units and the three blinking byte meanings are unknown. Port information occupies 152 bytes; history records occupy 72 bytes; Leonardo unique data occupies 24 bytes. Their complete field semantics remain unresolved.

  • libScePad profile writes and virtual devices - profile writes take a handle, profile ID, profile pointer and a 32-bit result pointer; the profile payload direction and fields remain unresolved. Virtual-device creation takes an input record and an output ID pointer, whose width is not exposed by the wrapper. Component information copies 16 bytes; field meanings remain unknown. Virtual trigger state contains two 32-bit states and three control bytes, but the control semantics remain incomplete.

Functional

  • Touchpad resolution (libScePad) - the library reports a DualSense to every title, so the controller information reports its touchpad, 1920 by 1080 at 44.86 pixels per cm, and the host controller's fingers and the keyboard touch contacts are placed in that range. duaLib reports these values for a DualSense and 1920 by 943 at 44 for a DualShock 4, and the Linux kernel's hid-playstation holds 1920 by 1080 against 1920 by 942 for the DualShock 4. A DualShock 4 on the host reports through the DualSense range too, so its last touchpad rows are never reached. A PS5 on firmware 13.40 reported 1920 by 1080 at 44.86 and stick dead zones of 13 to a retail title