mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-02-09 01:04:46 +01:00
Merge pull request #75 from ga2mer/audiodevice
Audio daemon playback support
This commit is contained in:
commit
f838dbbeae
6
.github/BUILDING.md
vendored
6
.github/BUILDING.md
vendored
|
|
@ -4,20 +4,20 @@
|
|||
### The dependencies for Debian-like distributions.
|
||||
|
||||
```
|
||||
sudo apt install build-essential cmake libunwind-dev libglfw3-dev libvulkan-dev vulkan-validationlayers-dev spirv-tools glslang-tools libspirv-cross-c-shared-dev libsox-dev git
|
||||
sudo apt install build-essential cmake libunwind-dev libglfw3-dev libvulkan-dev vulkan-validationlayers-dev spirv-tools glslang-tools libspirv-cross-c-shared-dev libsox-dev git libasound2-dev
|
||||
```
|
||||
# git is only needed for ubuntu 22.04
|
||||
|
||||
### The dependencies for Fedora distributions:
|
||||
|
||||
```
|
||||
sudo dnf install cmake libunwind-devel glfw-devel vulkan-devel vulkan-validation-layers-devel spirv-tools glslang-devel gcc-c++ gcc spirv-tools-devel xbyak-devel sox-devel
|
||||
sudo dnf install cmake libunwind-devel glfw-devel vulkan-devel vulkan-validation-layers-devel spirv-tools glslang-devel gcc-c++ gcc spirv-tools-devel xbyak-devel sox-devel alsa-lib-devel
|
||||
```
|
||||
|
||||
### The dependencies for Arch distributions:
|
||||
|
||||
```
|
||||
sudo pacman -S libunwind glfw-x11 vulkan-devel sox glslang git cmake
|
||||
sudo pacman -S libunwind glfw-x11 vulkan-devel sox glslang git cmake alsa-lib
|
||||
```
|
||||
> Side note you will need to pull ``spirv-cross`` from the AUR for now so do the following
|
||||
```
|
||||
|
|
|
|||
2
.github/workflows/rpcsx.yml
vendored
2
.github/workflows/rpcsx.yml
vendored
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
sudo apt update
|
||||
sudo apt install -y cmake build-essential libunwind-dev \
|
||||
libglfw3-dev libvulkan-dev vulkan-validationlayers \
|
||||
libsox-dev
|
||||
libsox-dev libasound2-dev
|
||||
echo "deb http://azure.archive.ubuntu.com/ubuntu noble main universe" | sudo tee /etc/apt/sources.list
|
||||
sudo apt update
|
||||
sudo apt install g++-14 ninja-build
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ target_include_directories(standalone-config INTERFACE orbis-kernel-config)
|
|||
add_library(orbis::kernel::config ALIAS standalone-config)
|
||||
|
||||
add_executable(rpcsx-os
|
||||
audio/AudioDevice.cpp
|
||||
audio/AlsaDevice.cpp
|
||||
|
||||
iodev/ajm.cpp
|
||||
iodev/blockpool.cpp
|
||||
iodev/bt.cpp
|
||||
|
|
@ -66,7 +69,7 @@ add_executable(rpcsx-os
|
|||
)
|
||||
|
||||
target_include_directories(rpcsx-os PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge rx libcrypto libunwind::unwind-x86_64 xbyak::xbyak sox::sox)
|
||||
target_link_libraries(rpcsx-os PUBLIC orbis::kernel amdgpu::bridge rx libcrypto libunwind::unwind-x86_64 xbyak::xbyak sox::sox asound)
|
||||
target_base_address(rpcsx-os 0x0000010000000000)
|
||||
target_compile_options(rpcsx-os PRIVATE "-mfsgsbase")
|
||||
|
||||
|
|
|
|||
242
rpcsx-os/audio/AlsaDevice.cpp
Normal file
242
rpcsx-os/audio/AlsaDevice.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
#include "AlsaDevice.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include "rx/hexdump.hpp"
|
||||
|
||||
AlsaDevice::AlsaDevice() {}
|
||||
|
||||
void AlsaDevice::start() {
|
||||
setAlsaFormat();
|
||||
int err;
|
||||
if ((err = snd_pcm_open(&mPCMHandle, "default", SND_PCM_STREAM_PLAYBACK,
|
||||
0)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot open audio device", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_malloc(&mHWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot allocate hardware parameter structure",
|
||||
snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_any(mPCMHandle, mHWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot initialize hardware parameter structure",
|
||||
snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_rate_resample(mPCMHandle, mHWParams,
|
||||
0)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot disable rate resampling", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_access(mPCMHandle, mHWParams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set access type", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_format(mPCMHandle, mHWParams,
|
||||
mAlsaFormat)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set sample format", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_rate(mPCMHandle, mHWParams, mFrequency,
|
||||
0)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set sample rate", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_channels(mPCMHandle, mHWParams, mChannels)) <
|
||||
0) {
|
||||
ORBIS_LOG_FATAL("cannot set channel count", snd_strerror(err), mChannels);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
uint periods = mSampleCount;
|
||||
if ((err = snd_pcm_hw_params_set_periods_max(mPCMHandle, mHWParams, &periods, NULL)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set periods count", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;
|
||||
|
||||
snd_pcm_uframes_t size = mSampleSize / frameBytes;
|
||||
|
||||
// TODO: it shouldn't work like this
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_buffer_size(mPCMHandle, mHWParams, size)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set buffer size", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_period_size(mPCMHandle, mHWParams, size / 2, 0)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot set period size", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t periodSize;
|
||||
if ((err = snd_pcm_hw_params_get_period_size(mHWParams, &periodSize, NULL)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t bufferSize;
|
||||
if ((err = snd_pcm_hw_params_get_buffer_size(mHWParams, &bufferSize)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
ORBIS_LOG_TODO("period and buffer", periodSize, bufferSize);
|
||||
|
||||
if ((err = snd_pcm_hw_params(mPCMHandle, mHWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params_malloc(&mSWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("Cannot allocate software parameter structure",
|
||||
snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params_current(mPCMHandle, mSWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot sw params current", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params_set_start_threshold(mPCMHandle, mSWParams, periodSize)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set start threshold", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params_set_stop_threshold(mPCMHandle, mSWParams, bufferSize)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set stop threshold", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_sw_params(mPCMHandle, mSWParams)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot set parameters", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if ((err = snd_pcm_prepare(mPCMHandle)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot prepare audio interface for use",
|
||||
snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
mWorking = true;
|
||||
}
|
||||
|
||||
int AlsaDevice::fixXRun()
|
||||
{
|
||||
switch (snd_pcm_state(mPCMHandle)) {
|
||||
case SND_PCM_STATE_XRUN:
|
||||
return snd_pcm_prepare(mPCMHandle);
|
||||
case SND_PCM_STATE_DRAINING:
|
||||
if (snd_pcm_stream(mPCMHandle) == SND_PCM_STREAM_CAPTURE)
|
||||
return snd_pcm_prepare(mPCMHandle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int AlsaDevice::resumeFromSupsend()
|
||||
{
|
||||
int res;
|
||||
while ((res = snd_pcm_resume(mPCMHandle)) == -EAGAIN)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
if (!res)
|
||||
return 0;
|
||||
return snd_pcm_prepare(mPCMHandle);
|
||||
}
|
||||
|
||||
long AlsaDevice::write(void *buf, long len) {
|
||||
if (!mWorking) return 0;
|
||||
ssize_t r;
|
||||
int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;
|
||||
snd_pcm_uframes_t frames = len / frameBytes;
|
||||
|
||||
r = snd_pcm_writei(mPCMHandle, buf, frames);
|
||||
if (r == -EPIPE) {
|
||||
if (!(r = fixXRun()))
|
||||
return write(buf, len);
|
||||
} else if (r == -ESTRPIPE) {
|
||||
if (!(r = resumeFromSupsend()))
|
||||
return write(buf, len);
|
||||
}
|
||||
r *= frameBytes;
|
||||
return r;
|
||||
}
|
||||
|
||||
void AlsaDevice::stop() {
|
||||
snd_pcm_hw_params_free(mHWParams);
|
||||
snd_pcm_sw_params_free(mSWParams);
|
||||
snd_pcm_drain(mPCMHandle);
|
||||
snd_pcm_drop(mPCMHandle);
|
||||
mWorking = false;
|
||||
}
|
||||
|
||||
void AlsaDevice::reset() {
|
||||
if (!mWorking) return;
|
||||
int err;
|
||||
err = snd_pcm_drop(mPCMHandle);
|
||||
if (err >= 0)
|
||||
err = snd_pcm_prepare(mPCMHandle);
|
||||
if (err < 0)
|
||||
err = err;
|
||||
}
|
||||
|
||||
audio_buf_info AlsaDevice::getOSpace() {
|
||||
int err;
|
||||
snd_pcm_uframes_t periodSize;
|
||||
if ((err = snd_pcm_hw_params_get_period_size(mHWParams, &periodSize, NULL)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot get period size", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
snd_pcm_uframes_t bufferSize;
|
||||
if ((err = snd_pcm_hw_params_get_buffer_size(mHWParams, &bufferSize)) < 0) {
|
||||
ORBIS_LOG_FATAL("cannot get buffer size", snd_strerror(err));
|
||||
std::abort();
|
||||
}
|
||||
int frameBytes = snd_pcm_format_physical_width(mAlsaFormat) * mChannels / 8;
|
||||
|
||||
snd_pcm_sframes_t avail, delay;
|
||||
audio_buf_info info;
|
||||
avail = snd_pcm_avail_update(mPCMHandle);
|
||||
if (avail < 0 || (snd_pcm_uframes_t)avail > bufferSize)
|
||||
avail = bufferSize;
|
||||
info.fragsize = periodSize * frameBytes;
|
||||
info.fragstotal = mSampleCount;
|
||||
info.bytes = avail * frameBytes;
|
||||
info.fragments = avail / periodSize;
|
||||
return info;
|
||||
}
|
||||
|
||||
void AlsaDevice::setAlsaFormat() {
|
||||
if (mWorking)
|
||||
return;
|
||||
_snd_pcm_format fmt;
|
||||
switch (mFormat) {
|
||||
case FMT_S32_LE:
|
||||
fmt = SND_PCM_FORMAT_S32_LE;
|
||||
break;
|
||||
case FMT_S16_LE:
|
||||
fmt = SND_PCM_FORMAT_S16_LE;
|
||||
break;
|
||||
case FMT_AC3:
|
||||
default:
|
||||
ORBIS_LOG_FATAL("Format is not supported", mFormat);
|
||||
std::abort();
|
||||
break;
|
||||
}
|
||||
mAlsaFormat = fmt;
|
||||
}
|
||||
|
||||
AlsaDevice::~AlsaDevice() {
|
||||
stop();
|
||||
}
|
||||
31
rpcsx-os/audio/AlsaDevice.hpp
Normal file
31
rpcsx-os/audio/AlsaDevice.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "AudioDevice.hpp"
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
|
||||
class AlsaDevice : public AudioDevice {
|
||||
private:
|
||||
snd_pcm_format_t mAlsaFormat;
|
||||
snd_pcm_t *mPCMHandle;
|
||||
snd_pcm_hw_params_t *mHWParams;
|
||||
snd_pcm_sw_params_t *mSWParams;
|
||||
|
||||
public:
|
||||
AlsaDevice();
|
||||
~AlsaDevice() override;
|
||||
|
||||
void init() override {};
|
||||
void start() override;
|
||||
long write(void *, long) override;
|
||||
void stop() override;
|
||||
void reset() override;
|
||||
|
||||
void setAlsaFormat();
|
||||
|
||||
int fixXRun();
|
||||
int resumeFromSupsend();
|
||||
|
||||
audio_buf_info getOSpace() override;
|
||||
};
|
||||
55
rpcsx-os/audio/AudioDevice.cpp
Normal file
55
rpcsx-os/audio/AudioDevice.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#include "AudioDevice.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include "rx/hexdump.hpp"
|
||||
|
||||
AudioDevice::AudioDevice() {}
|
||||
|
||||
void AudioDevice::init() {}
|
||||
|
||||
void AudioDevice::start() {}
|
||||
|
||||
long AudioDevice::write(void *buf, long len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void AudioDevice::stop() {
|
||||
}
|
||||
|
||||
void AudioDevice::reset() {}
|
||||
|
||||
void AudioDevice::setFormat(orbis::uint format) {
|
||||
if (mWorking)
|
||||
return;
|
||||
mFormat = format;
|
||||
}
|
||||
|
||||
void AudioDevice::setFrequency(orbis::uint frequency) {
|
||||
if (mWorking)
|
||||
return;
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
void AudioDevice::setChannels(orbis::ushort channels) {
|
||||
if (mWorking)
|
||||
return;
|
||||
if (channels > 8) {
|
||||
ORBIS_LOG_FATAL("Channels count is not supported", channels);
|
||||
std::abort();
|
||||
}
|
||||
mChannels = channels;
|
||||
}
|
||||
|
||||
void AudioDevice::setSampleSize(orbis::uint sampleSize, orbis::uint sampleCount) {
|
||||
if (mWorking)
|
||||
return;
|
||||
mSampleSize = sampleSize;
|
||||
mSampleCount = sampleCount;
|
||||
}
|
||||
|
||||
audio_buf_info AudioDevice::getOSpace() {
|
||||
audio_buf_info info;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
AudioDevice::~AudioDevice() {}
|
||||
44
rpcsx-os/audio/AudioDevice.hpp
Normal file
44
rpcsx-os/audio/AudioDevice.hpp
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#define FMT_S16_LE 0x10
|
||||
#define FMT_AC3 0x400
|
||||
#define FMT_S32_LE 0x1000
|
||||
|
||||
#include <cstdlib>
|
||||
#include <orbis/sys/sysproto.hpp>
|
||||
|
||||
struct audio_buf_info {
|
||||
int fragments;
|
||||
int fragstotal;
|
||||
int fragsize;
|
||||
int bytes;
|
||||
};
|
||||
|
||||
class AudioDevice {
|
||||
protected:
|
||||
bool mWorking = false;
|
||||
orbis::uint mFormat{};
|
||||
orbis::uint mFrequency{};
|
||||
orbis::ushort mChannels{};
|
||||
orbis::ushort mSampleSize{};
|
||||
orbis::ushort mSampleCount{};
|
||||
|
||||
private:
|
||||
|
||||
public:
|
||||
AudioDevice();
|
||||
virtual ~AudioDevice();
|
||||
|
||||
virtual void init();
|
||||
virtual void start();
|
||||
virtual long write(void *buf, long len);
|
||||
virtual void stop();
|
||||
virtual void reset();
|
||||
|
||||
void setFormat(orbis::uint format);
|
||||
void setFrequency(orbis::uint frequency);
|
||||
void setChannels(orbis::ushort channels);
|
||||
void setSampleSize(orbis::uint sampleSize = 0, orbis::uint sampleCount = 0);
|
||||
|
||||
virtual audio_buf_info getOSpace();
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ IoDevice *createXptCharacterDevice();
|
|||
IoDevice *createCdCharacterDevice();
|
||||
IoDevice *createMetaDbgCharacterDevice();
|
||||
IoDevice *createHddCharacterDevice(std::uint64_t size);
|
||||
IoDevice *createAoutCharacterDevice();
|
||||
IoDevice *createAoutCharacterDevice(std::int8_t id);
|
||||
IoDevice *createAVControlCharacterDevice();
|
||||
IoDevice *createHDMICharacterDevice();
|
||||
IoDevice *createMBusAVCharacterDevice();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "audio/AlsaDevice.hpp"
|
||||
#include "io-device.hpp"
|
||||
#include "iodev/mbus_av.hpp"
|
||||
#include "orbis/KernelAllocator.hpp"
|
||||
|
|
@ -8,20 +9,168 @@
|
|||
#include "orbis/uio.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include <bits/types/struct_iovec.h>
|
||||
// #include <rx/hexdump.hpp>
|
||||
|
||||
#define SNDCTL_DSP_RESET 0x20005000
|
||||
#define SNDCTL_DSP_SETFRAGMENT 0xc004500a
|
||||
#define SNDCTL_DSP_SETFMT 0xc0045005
|
||||
#define SNDCTL_DSP_SPEED 0xc0045002
|
||||
#define SNDCTL_DSP_CHANNELS 0xc0045006
|
||||
#define ORBIS_AUDIO_UPDATE_TICK_PARAMS 0xc004505c
|
||||
#define SNDCTL_DSP_SYNCGROUP 0xc048501c
|
||||
#define ORBIS_AUDIO_CONFIG_SPDIF 0xc0085063
|
||||
#define SNDCTL_DSP_GETBLKSIZE 0x40045004
|
||||
#define SOUND_PCM_READ_BITS 0x40045005
|
||||
#define SNDCTL_DSP_GETOSPACE 0x4010500c
|
||||
#define SNDCTL_DSP_SYNCSTART 0x8004501d
|
||||
#define ORBIS_AUDIO_IOCTL_SETCONTROL 0x80085062
|
||||
|
||||
struct AoutFile : orbis::File {};
|
||||
|
||||
struct AoutDevice : public IoDevice {
|
||||
std::int8_t id;
|
||||
AudioDevice *audioDevice;
|
||||
|
||||
AoutDevice(std::int8_t id) : id(id) {}
|
||||
|
||||
orbis::ErrorCode open(orbis::Ref<orbis::File> *file, const char *path,
|
||||
std::uint32_t flags, std::uint32_t mode,
|
||||
orbis::Thread *thread) override;
|
||||
};
|
||||
|
||||
static orbis::ErrorCode aout_ioctl(orbis::File *file, std::uint64_t request,
|
||||
void *argp, orbis::Thread *thread) {
|
||||
ORBIS_LOG_FATAL("Unhandled aout ioctl", request);
|
||||
thread->where();
|
||||
auto device = static_cast<AoutDevice *>(file->device.get());
|
||||
switch (request) {
|
||||
case SNDCTL_DSP_RESET: {
|
||||
ORBIS_LOG_TODO("SNDCTL_DSP_RESET");
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->reset();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_SETFRAGMENT: {
|
||||
struct Args {
|
||||
std::uint32_t fragment;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("SNDCTL_DSP_SETFRAGMENT", args->fragment & 0xF, (args->fragment >> 16) & 0xF);
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->setSampleSize(1 << (args->fragment & 0xF), (args->fragment >> 16) & 0xF);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_SETFMT: {
|
||||
struct Args {
|
||||
std::uint32_t fmt;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("SNDCTL_DSP_SETFMT", args->fmt);
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->setFormat(args->fmt);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_SPEED: {
|
||||
struct Args {
|
||||
std::uint32_t speed;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->setFrequency(args->speed);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_CHANNELS: {
|
||||
struct Args {
|
||||
std::uint32_t channels;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->setChannels(args->channels);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case ORBIS_AUDIO_UPDATE_TICK_PARAMS: {
|
||||
struct Args {
|
||||
std::uint32_t tick;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("ORBIS_AUDIO_UPDATE_TICK_PARAMS", args->tick);
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_SYNCGROUP: {
|
||||
ORBIS_LOG_NOTICE("SNDCTL_DSP_SYNCGROUP");
|
||||
return {};
|
||||
}
|
||||
case ORBIS_AUDIO_CONFIG_SPDIF: {
|
||||
struct Args {
|
||||
std::uint64_t unk0;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
args->unk0 = 0x100000000; // Disable SPDIF output
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_GETBLKSIZE: {
|
||||
struct Args {
|
||||
std::uint32_t blksize;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("SNDCTL_DSP_GETBLKSIZE", args->blksize);
|
||||
return {};
|
||||
}
|
||||
case SOUND_PCM_READ_BITS: {
|
||||
struct Args {
|
||||
std::uint32_t bits;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("SOUND_PCM_READ_BITS", args->bits);
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_GETOSPACE: {
|
||||
auto args = reinterpret_cast<audio_buf_info *>(argp);
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
auto info = audioDevice->getOSpace();
|
||||
args->fragments = info.fragments;
|
||||
args->fragstotal = info.fragstotal;
|
||||
args->fragsize = info.fragsize;
|
||||
args->bytes = info.bytes;
|
||||
}
|
||||
ORBIS_LOG_TODO("SNDCTL_DSP_GETOSPACE", args->fragments, args->fragstotal, args->fragsize, args->bytes);
|
||||
return {};
|
||||
}
|
||||
case SNDCTL_DSP_SYNCSTART: {
|
||||
ORBIS_LOG_NOTICE("SNDCTL_DSP_SYNCSTART");
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
audioDevice->start();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case ORBIS_AUDIO_IOCTL_SETCONTROL: {
|
||||
struct Args {
|
||||
std::uint64_t unk0;
|
||||
};
|
||||
auto args = reinterpret_cast<Args *>(argp);
|
||||
ORBIS_LOG_NOTICE("ORBIS_AUDIO_IOCTL_SETCONTROL", args->unk0);
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
ORBIS_LOG_FATAL("Unhandled aout ioctl", request);
|
||||
thread->where();
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static orbis::ErrorCode aout_write(orbis::File *file, orbis::Uio *uio,
|
||||
orbis::Thread *) {
|
||||
for (auto entry : std::span(uio->iov, uio->iovcnt)) {
|
||||
uio->offset += entry.len;
|
||||
orbis::Thread *thread) {
|
||||
auto device = static_cast<AoutDevice *>(file->device.get());
|
||||
if (auto audioDevice = device->audioDevice) {
|
||||
for (auto vec : std::span(uio->iov, uio->iovcnt)) {
|
||||
audioDevice->write(vec.base, vec.len);
|
||||
// rx::hexdump({(std::byte*)vec.base, vec.len});
|
||||
uio->offset += vec.len;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
@ -31,19 +180,24 @@ static const orbis::FileOps fileOps = {
|
|||
.write = aout_write,
|
||||
};
|
||||
|
||||
struct AoutDevice : IoDevice {
|
||||
orbis::ErrorCode open(orbis::Ref<orbis::File> *file, const char *path,
|
||||
std::uint32_t flags, std::uint32_t mode,
|
||||
orbis::Thread *thread) override {
|
||||
ORBIS_LOG_FATAL("aout device open", path, flags, mode);
|
||||
auto newFile = orbis::knew<AoutFile>();
|
||||
newFile->ops = &fileOps;
|
||||
newFile->device = this;
|
||||
thread->where();
|
||||
orbis::ErrorCode AoutDevice::open(orbis::Ref<orbis::File> *file,
|
||||
const char *path, std::uint32_t flags,
|
||||
std::uint32_t mode, orbis::Thread *thread) {
|
||||
ORBIS_LOG_FATAL("aout device open", path, flags, mode);
|
||||
auto newFile = orbis::knew<AoutFile>();
|
||||
newFile->ops = &fileOps;
|
||||
newFile->device = this;
|
||||
thread->where();
|
||||
|
||||
*file = newFile;
|
||||
return {};
|
||||
*file = newFile;
|
||||
// create audio device only for hdmi output, 0 - hdmi, 1 - analog, 2 - spdif
|
||||
if (id == 0) {
|
||||
// TODO: use factory to more backends support
|
||||
audioDevice = new AlsaDevice();
|
||||
}
|
||||
};
|
||||
return {};
|
||||
}
|
||||
|
||||
IoDevice *createAoutCharacterDevice() { return orbis::knew<AoutDevice>(); }
|
||||
IoDevice *createAoutCharacterDevice(std::int8_t id) {
|
||||
return orbis::knew<AoutDevice>(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -404,9 +404,9 @@ static void ps4InitDev() {
|
|||
rx::vfs::addDevice("notification3", createNotificationCharacterDevice(3));
|
||||
rx::vfs::addDevice("notification4", createNotificationCharacterDevice(4));
|
||||
rx::vfs::addDevice("notification5", createNotificationCharacterDevice(5));
|
||||
rx::vfs::addDevice("aout0", createAoutCharacterDevice());
|
||||
rx::vfs::addDevice("aout1", createAoutCharacterDevice());
|
||||
rx::vfs::addDevice("aout2", createAoutCharacterDevice());
|
||||
rx::vfs::addDevice("aout0", createAoutCharacterDevice(0));
|
||||
rx::vfs::addDevice("aout1", createAoutCharacterDevice(1));
|
||||
rx::vfs::addDevice("aout2", createAoutCharacterDevice(2));
|
||||
rx::vfs::addDevice("av_control", createAVControlCharacterDevice());
|
||||
rx::vfs::addDevice("hdmi", createHDMICharacterDevice());
|
||||
rx::vfs::addDevice("mbus_av", mbusAv);
|
||||
|
|
@ -464,6 +464,189 @@ static void ps4InitFd(orbis::Thread *mainThread) {
|
|||
mainThread->tproc->fileDescriptors.insert(stderrFile);
|
||||
}
|
||||
|
||||
static orbis::Process *createGuestProcess() {
|
||||
auto pid = orbis::g_context.allocatePid() * 10000 + 1;
|
||||
return orbis::g_context.createProcess(pid);
|
||||
}
|
||||
|
||||
static orbis::Thread *createGuestThread() {
|
||||
auto process = createGuestProcess();
|
||||
auto [baseId, thread] = process->threadsMap.emplace();
|
||||
thread->tproc = process;
|
||||
thread->tid = process->pid + baseId;
|
||||
thread->state = orbis::ThreadState::RUNNING;
|
||||
return thread;
|
||||
}
|
||||
|
||||
template <typename T = std::byte> struct GuestAlloc {
|
||||
orbis::ptr<T> guestAddress;
|
||||
|
||||
GuestAlloc(std::size_t size) {
|
||||
if (size == 0) {
|
||||
guestAddress = nullptr;
|
||||
} else {
|
||||
guestAddress = orbis::ptr<T>(rx::vm::map(
|
||||
nullptr, size, rx::vm::kMapProtCpuRead | rx::vm::kMapProtCpuWrite,
|
||||
rx::vm::kMapFlagPrivate | rx::vm::kMapFlagAnonymous));
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc() : GuestAlloc(sizeof(T)) {}
|
||||
|
||||
GuestAlloc(const T &data) : GuestAlloc() {
|
||||
if (orbis::uwrite(guestAddress, data) != orbis::ErrorCode{}) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc(const void *data, std::size_t size) : GuestAlloc(size) {
|
||||
if (orbis::uwriteRaw(guestAddress, data, size) != orbis::ErrorCode{}) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc(const GuestAlloc &) = delete;
|
||||
|
||||
GuestAlloc(GuestAlloc &&other) : guestAddress(other.guestAddress) {
|
||||
other.guestAddress = 0;
|
||||
}
|
||||
GuestAlloc &operator=(GuestAlloc &&other) {
|
||||
std::swap(guestAddress, other.guestAddress);
|
||||
}
|
||||
|
||||
~GuestAlloc() {
|
||||
if (guestAddress != 0) {
|
||||
rx::vm::unmap(guestAddress, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
operator orbis::ptr<T>() { return guestAddress; }
|
||||
T *operator->() { return guestAddress; }
|
||||
operator T &() { return *guestAddress; }
|
||||
};
|
||||
|
||||
struct IpmiClient {
|
||||
orbis::Ref<orbis::IpmiClient> clientImpl;
|
||||
orbis::uint kid;
|
||||
orbis::Thread *thread;
|
||||
|
||||
orbis::sint
|
||||
sendSyncMessageRaw(std::uint32_t method,
|
||||
const std::vector<std::vector<std::byte>> &inData,
|
||||
std::vector<std::vector<std::byte>> &outBuf) {
|
||||
GuestAlloc<orbis::sint> serverResult;
|
||||
GuestAlloc<orbis::IpmiDataInfo> guestInDataArray{
|
||||
sizeof(orbis::IpmiDataInfo) * inData.size()};
|
||||
GuestAlloc<orbis::IpmiBufferInfo> guestOutBufArray{
|
||||
sizeof(orbis::IpmiBufferInfo) * outBuf.size()};
|
||||
|
||||
std::vector<GuestAlloc<std::byte>> guestAllocs;
|
||||
guestAllocs.reserve(inData.size() + outBuf.size());
|
||||
|
||||
for (auto &data : inData) {
|
||||
auto pointer =
|
||||
guestAllocs.emplace_back(data.data(), data.size()).guestAddress;
|
||||
|
||||
guestInDataArray.guestAddress[&data - inData.data()] = {
|
||||
.data = pointer, .size = data.size()};
|
||||
}
|
||||
|
||||
for (auto &buf : outBuf) {
|
||||
auto pointer =
|
||||
guestAllocs.emplace_back(buf.data(), buf.size()).guestAddress;
|
||||
|
||||
guestOutBufArray.guestAddress[&buf - outBuf.data()] = {
|
||||
.data = pointer, .capacity = buf.size()};
|
||||
}
|
||||
|
||||
GuestAlloc params = orbis::IpmiSyncCallParams{
|
||||
.method = method,
|
||||
.numInData = static_cast<orbis::uint32_t>(inData.size()),
|
||||
.numOutData = static_cast<orbis::uint32_t>(outBuf.size()),
|
||||
.pInData = guestInDataArray,
|
||||
.pOutData = guestOutBufArray,
|
||||
.pResult = serverResult,
|
||||
.flags = (inData.size() >= 1 || outBuf.size() >= 1) ? 1u : 0u,
|
||||
};
|
||||
|
||||
GuestAlloc<orbis::uint> errorCode;
|
||||
orbis::sysIpmiClientInvokeSyncMethod(thread, errorCode, kid, params,
|
||||
sizeof(orbis::IpmiSyncCallParams));
|
||||
|
||||
for (auto &buf : outBuf) {
|
||||
auto size = guestOutBufArray.guestAddress[inData.data() - &buf].size;
|
||||
buf.resize(size);
|
||||
}
|
||||
return serverResult;
|
||||
}
|
||||
|
||||
template <typename... InputTypes>
|
||||
orbis::sint sendSyncMessage(std::uint32_t method,
|
||||
const InputTypes &...input) {
|
||||
std::vector<std::vector<std::byte>> outBuf;
|
||||
return sendSyncMessageRaw(method, {toBytes(input)...}, outBuf);
|
||||
}
|
||||
|
||||
template <typename... OutputTypes, typename... InputTypes>
|
||||
requires((sizeof...(OutputTypes) > 0) || sizeof...(InputTypes) == 0)
|
||||
std::tuple<OutputTypes...> sendSyncMessage(std::uint32_t method,
|
||||
InputTypes... input) {
|
||||
std::vector<std::vector<std::byte>> outBuf{sizeof(OutputTypes)...};
|
||||
sendSyncMessageRaw(method, {toBytes(input)...}, outBuf);
|
||||
std::tuple<OutputTypes...> output;
|
||||
|
||||
auto unpack = [&]<std::size_t... I>(std::index_sequence<I...>) {
|
||||
((std::get<I>(output) = *reinterpret_cast<OutputTypes *>(outBuf.data())),
|
||||
...);
|
||||
};
|
||||
unpack(std::make_index_sequence<sizeof...(OutputTypes)>{});
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
static IpmiClient audioIpmiClient;
|
||||
|
||||
static IpmiClient createIpmiClient(orbis::Thread *thread, const char *name) {
|
||||
orbis::Ref<orbis::IpmiClient> client;
|
||||
GuestAlloc config = orbis::IpmiCreateClientConfig{
|
||||
.size = sizeof(orbis::IpmiCreateClientConfig),
|
||||
};
|
||||
|
||||
orbis::uint kid;
|
||||
|
||||
{
|
||||
GuestAlloc<char> guestName{name, std::strlen(name)};
|
||||
GuestAlloc params = orbis::IpmiCreateClientParams{
|
||||
.name = guestName,
|
||||
.config = config,
|
||||
};
|
||||
|
||||
GuestAlloc<orbis::uint> result;
|
||||
GuestAlloc<orbis::uint> guestKid;
|
||||
orbis::sysIpmiCreateClient(thread, guestKid, params,
|
||||
sizeof(orbis::IpmiCreateClientParams));
|
||||
kid = guestKid;
|
||||
}
|
||||
|
||||
{
|
||||
GuestAlloc<orbis::sint> status;
|
||||
GuestAlloc params = orbis::IpmiClientConnectParams{.status = status};
|
||||
|
||||
GuestAlloc<orbis::uint> result;
|
||||
while (true) {
|
||||
auto errc = orbis::sysIpmiClientConnect(
|
||||
thread, result, kid, params, sizeof(orbis::IpmiClientConnectParams));
|
||||
if (errc.value() == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(300));
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(client), kid, thread};
|
||||
}
|
||||
|
||||
struct ExecEnv {
|
||||
std::uint64_t entryPoint;
|
||||
std::uint64_t interpBase;
|
||||
|
|
@ -1281,7 +1464,7 @@ static void createAudioSystemObjects(orbis::Process *process) {
|
|||
}
|
||||
|
||||
struct SceMbusIpcAddHandleByUserIdMethodArgs {
|
||||
orbis::uint32_t unk; // 0
|
||||
orbis::uint32_t deviceType; // 0 - pad, 1 - aout, 2 - ain, 4 - camera, 6 - kb, 7 - mouse, 8 - vr
|
||||
orbis::uint32_t deviceId;
|
||||
orbis::uint32_t userId;
|
||||
orbis::uint32_t type;
|
||||
|
|
@ -1301,9 +1484,36 @@ static void createSysCoreObjects(orbis::Process *process) {
|
|||
createIpmiServer(process, "SceMbusIpc")
|
||||
.addSyncMethod<SceMbusIpcAddHandleByUserIdMethodArgs>(
|
||||
0xce110007, [](const auto &args) -> std::int32_t {
|
||||
ORBIS_LOG_TODO("IPMI: SceMbusIpcAddHandleByUserId", args.unk,
|
||||
ORBIS_LOG_TODO("IPMI: SceMbusIpcAddHandleByUserId", args.deviceType,
|
||||
args.deviceId, args.userId, args.type, args.index,
|
||||
args.reserved, args.pid);
|
||||
if (args.deviceType == 1) {
|
||||
struct HandleA {
|
||||
int32_t pid;
|
||||
int32_t port;
|
||||
int32_t unk0 = 0x20100000;
|
||||
int32_t unk1 = 1;
|
||||
} handleA;
|
||||
handleA.pid = args.pid;
|
||||
handleA.port = args.deviceId;
|
||||
audioIpmiClient.sendSyncMessage(0x1234000a, handleA);
|
||||
struct HandleC {
|
||||
int32_t pid;
|
||||
int32_t port;
|
||||
int32_t unk0 = 1;
|
||||
int32_t unk1 = 0;
|
||||
int32_t unk2 = 1;
|
||||
int32_t unk3 = 0;
|
||||
int32_t unk4 = 0;
|
||||
int32_t unk5 = 0;
|
||||
int32_t unk6 = 0;
|
||||
int32_t unk7 = 1;
|
||||
int32_t unk8 = 0;
|
||||
} handleC;
|
||||
handleC.pid = args.pid;
|
||||
handleC.port = args.deviceId;
|
||||
audioIpmiClient.sendSyncMessage(0x1234000c, handleC);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
createIpmiServer(process, "SceSysCoreApp");
|
||||
|
|
@ -1619,180 +1829,6 @@ static void createShellCoreObjects(orbis::Process *process) {
|
|||
createSemaphore("SceNpTpip 0", 0x101, 0, 1);
|
||||
}
|
||||
|
||||
static orbis::Process *createGuestProcess() {
|
||||
auto pid = orbis::g_context.allocatePid() * 10000 + 1;
|
||||
return orbis::g_context.createProcess(pid);
|
||||
}
|
||||
|
||||
static orbis::Thread *createGuestThread() {
|
||||
auto process = createGuestProcess();
|
||||
auto [baseId, thread] = process->threadsMap.emplace();
|
||||
thread->tproc = process;
|
||||
thread->tid = process->pid + baseId;
|
||||
thread->state = orbis::ThreadState::RUNNING;
|
||||
return thread;
|
||||
}
|
||||
|
||||
template <typename T = std::byte> struct GuestAlloc {
|
||||
orbis::ptr<T> guestAddress;
|
||||
|
||||
GuestAlloc(std::size_t size) {
|
||||
if (size == 0) {
|
||||
guestAddress = nullptr;
|
||||
} else {
|
||||
guestAddress = orbis::ptr<T>(rx::vm::map(
|
||||
nullptr, size, rx::vm::kMapProtCpuRead | rx::vm::kMapProtCpuWrite,
|
||||
rx::vm::kMapFlagPrivate | rx::vm::kMapFlagAnonymous));
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc() : GuestAlloc(sizeof(T)) {}
|
||||
|
||||
GuestAlloc(const T &data) : GuestAlloc() {
|
||||
if (orbis::uwrite(guestAddress, data) != orbis::ErrorCode{}) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc(const void *data, std::size_t size) : GuestAlloc(size) {
|
||||
if (orbis::uwriteRaw(guestAddress, data, size) != orbis::ErrorCode{}) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
GuestAlloc(const GuestAlloc &) = delete;
|
||||
|
||||
GuestAlloc(GuestAlloc &&other) : guestAddress(other.guestAddress) {
|
||||
other.guestAddress = 0;
|
||||
}
|
||||
GuestAlloc &operator=(GuestAlloc &&other) {
|
||||
std::swap(guestAddress, other.guestAddress);
|
||||
}
|
||||
|
||||
~GuestAlloc() {
|
||||
if (guestAddress != 0) {
|
||||
rx::vm::unmap(guestAddress, sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
operator orbis::ptr<T>() { return guestAddress; }
|
||||
T *operator->() { return guestAddress; }
|
||||
operator T &() { return *guestAddress; }
|
||||
};
|
||||
|
||||
struct IpmiClient {
|
||||
orbis::Ref<orbis::IpmiClient> clientImpl;
|
||||
orbis::uint kid;
|
||||
orbis::Thread *thread;
|
||||
|
||||
orbis::sint
|
||||
sendSyncMessageRaw(std::uint32_t method,
|
||||
const std::vector<std::vector<std::byte>> &inData,
|
||||
std::vector<std::vector<std::byte>> &outBuf) {
|
||||
GuestAlloc<orbis::sint> serverResult;
|
||||
GuestAlloc<orbis::IpmiDataInfo> guestInDataArray{
|
||||
sizeof(orbis::IpmiDataInfo) * inData.size()};
|
||||
GuestAlloc<orbis::IpmiBufferInfo> guestOutBufArray{
|
||||
sizeof(orbis::IpmiBufferInfo) * outBuf.size()};
|
||||
|
||||
std::vector<GuestAlloc<std::byte>> guestAllocs;
|
||||
guestAllocs.reserve(inData.size() + outBuf.size());
|
||||
|
||||
for (auto &data : inData) {
|
||||
auto pointer =
|
||||
guestAllocs.emplace_back(data.data(), data.size()).guestAddress;
|
||||
|
||||
guestInDataArray.guestAddress[&data - inData.data()] = {
|
||||
.data = pointer, .size = data.size()};
|
||||
}
|
||||
|
||||
for (auto &buf : outBuf) {
|
||||
auto pointer =
|
||||
guestAllocs.emplace_back(buf.data(), buf.size()).guestAddress;
|
||||
|
||||
guestOutBufArray.guestAddress[&buf - outBuf.data()] = {
|
||||
.data = pointer, .capacity = buf.size()};
|
||||
}
|
||||
|
||||
GuestAlloc params = orbis::IpmiSyncCallParams{
|
||||
.method = method,
|
||||
.numInData = static_cast<orbis::uint32_t>(inData.size()),
|
||||
.numOutData = static_cast<orbis::uint32_t>(outBuf.size()),
|
||||
.pInData = guestInDataArray,
|
||||
.pOutData = guestOutBufArray,
|
||||
.pResult = serverResult,
|
||||
.flags = (inData.size() > 1 || outBuf.size() > 1) ? 1u : 0u,
|
||||
};
|
||||
|
||||
GuestAlloc<orbis::uint> errorCode;
|
||||
orbis::sysIpmiClientInvokeSyncMethod(thread, errorCode, kid, params,
|
||||
sizeof(orbis::IpmiSyncCallParams));
|
||||
|
||||
for (auto &buf : outBuf) {
|
||||
auto size = guestOutBufArray.guestAddress[inData.data() - &buf].size;
|
||||
buf.resize(size);
|
||||
}
|
||||
return serverResult;
|
||||
}
|
||||
|
||||
template <typename... InputTypes>
|
||||
orbis::sint sendSyncMessage(std::uint32_t method,
|
||||
const InputTypes &...input) {
|
||||
std::vector<std::vector<std::byte>> outBuf;
|
||||
return sendSyncMessageRaw(method, {toBytes(input)...}, outBuf);
|
||||
}
|
||||
|
||||
template <typename... OutputTypes, typename... InputTypes>
|
||||
requires((sizeof...(OutputTypes) > 0) || sizeof...(InputTypes) == 0)
|
||||
std::tuple<OutputTypes...> sendSyncMessage(std::uint32_t method,
|
||||
InputTypes... input) {
|
||||
std::vector<std::vector<std::byte>> outBuf{sizeof(OutputTypes)...};
|
||||
sendSyncMessageRaw(method, {toBytes(input)...}, outBuf);
|
||||
std::tuple<OutputTypes...> output;
|
||||
|
||||
auto unpack = [&]<std::size_t... I>(std::index_sequence<I...>) {
|
||||
((std::get<I>(output) = *reinterpret_cast<OutputTypes *>(outBuf.data())),
|
||||
...);
|
||||
};
|
||||
unpack(std::make_index_sequence<sizeof...(OutputTypes)>{});
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
static IpmiClient createIpmiClient(orbis::Thread *thread, const char *name) {
|
||||
orbis::Ref<orbis::IpmiClient> client;
|
||||
GuestAlloc config = orbis::IpmiCreateClientConfig{
|
||||
.size = sizeof(orbis::IpmiCreateClientConfig),
|
||||
};
|
||||
|
||||
orbis::uint kid;
|
||||
|
||||
{
|
||||
GuestAlloc<char> guestName{name, std::strlen(name)};
|
||||
GuestAlloc params = orbis::IpmiCreateClientParams{
|
||||
.name = guestName,
|
||||
.config = config,
|
||||
};
|
||||
|
||||
GuestAlloc<orbis::uint> result;
|
||||
GuestAlloc<orbis::uint> guestKid;
|
||||
orbis::sysIpmiCreateClient(thread, guestKid, params,
|
||||
sizeof(orbis::IpmiCreateClientParams));
|
||||
kid = guestKid;
|
||||
}
|
||||
|
||||
{
|
||||
GuestAlloc<orbis::sint> status;
|
||||
GuestAlloc params = orbis::IpmiClientConnectParams{.status = status};
|
||||
|
||||
GuestAlloc<orbis::uint> result;
|
||||
orbis::sysIpmiClientConnect(thread, result, kid, params,
|
||||
sizeof(orbis::IpmiClientConnectParams));
|
||||
}
|
||||
|
||||
return {std::move(client), kid, thread};
|
||||
}
|
||||
|
||||
static orbis::SysResult launchDaemon(orbis::Thread *thread, std::string path,
|
||||
std::vector<std::string> argv,
|
||||
std::vector<std::string> envv,
|
||||
|
|
@ -2186,6 +2222,34 @@ int main(int argc, const char *argv[]) {
|
|||
.titleId = "NPXS20973",
|
||||
.unk4 = orbis::slong(0x80000000'00000000),
|
||||
});
|
||||
// confirmed to work and known method of initialization since 5.05 version
|
||||
if (orbis::g_context.fwSdkVersion >= 0x5050000) {
|
||||
auto fakeIpmiThread = createGuestThread();
|
||||
audioIpmiClient = createIpmiClient(fakeIpmiThread, "SceSysAudioSystemIpc");
|
||||
// HACK: here is a bug in audiod because we send this very early and audiod has time to reset the state due to initialization
|
||||
// so we wait for a second, during this time audiod should have time to initialize on most systems
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
struct Data1 {
|
||||
int32_t pid = 0;
|
||||
int32_t someSwitch = 0x14; // 0x14 for init, 0x19 for mute
|
||||
int32_t someFlag = 0;
|
||||
} data1;
|
||||
data1.pid = fakeIpmiThread->tproc->pid;
|
||||
struct Data2 {
|
||||
void* unk0 = 0;
|
||||
int32_t unk1 = 0x105;
|
||||
int32_t unk2 = 0x10000;
|
||||
int64_t unk3 = 0;
|
||||
int32_t unk4 = 0;
|
||||
int32_t unk5 = 0;
|
||||
int32_t unk6 = 0;
|
||||
int64_t unk7 = 0;
|
||||
int32_t unk8 = 0x2;
|
||||
char unk9[24]{0};
|
||||
} data2;
|
||||
std::uint32_t method = orbis::g_context.fwSdkVersion >= 0x8000000 ? 0x1234002c : 0x1234002b;
|
||||
audioIpmiClient.sendSyncMessage(method, data1, data2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue