error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id) { sys_ppu_thread.trace("sys_ppu_thread_start(thread_id=0x%x)", thread_id); const auto thread = idm::get<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread) { lv2_obj::awake(thread, -2); }); if (!thread) { return CELL_ESRCH; } if (!thread->state.test_and_reset(cpu_flag::stop)) { // TODO: what happens there? return CELL_EPERM; } else { thread_ctrl::notify(*thread); // Dirty hack for sound: confirm the creation of _mxr000 event queue if (thread->ppu_name.get() == "_cellsurMixerMain"sv) { lv2_obj::sleep(ppu); while (!idm::select<lv2_obj, lv2_event_queue>([](u32, lv2_event_queue& eq) { //some games do not set event queue name, though key seems constant for them return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300); })) { if (ppu.is_stopped()) { return 0; } thread_ctrl::wait_for(50000); } if (ppu.test_stopped()) { return 0; } } } return CELL_OK; }
error_code cellAudioQuit(ppu_thread& ppu) { cellAudio.warning("cellAudioQuit()"); // Stop audio thread auto g_audio = g_idm->lock<named_thread<audio_thread>>(0); if (!g_audio) { return CELL_AUDIO_ERROR_NOT_INIT; } // Signal to abort, release lock *g_audio.get() = thread_state::aborting; g_audio.unlock(); while (true) { if (ppu.is_stopped()) { return 0; } thread_ctrl::wait_for(1000); auto g_audio = g_idm->lock<named_thread<audio_thread>>(0); if (*g_audio.get() == thread_state::finished) { const auto buf = g_audio->ports[0].addr; const auto ind = g_audio->ports[0].index; g_audio.destroy(); g_audio.unlock(); vm::dealloc(buf.addr()); vm::dealloc(ind.addr()); break; } } return CELL_OK; }
// Note: the way we do things here is inaccurate(but maybe sufficient) // The real function goes over a table of 0x20 entries[ event_code:u32 callback_addr:u32 ] // Those callbacks are registered through cellSysutilRegisterCallbackDispatcher(u32 event_code, vm::ptr<void> func_addr) // The function goes through all the callback looking for one callback associated with event 0x100, if any is found it is called with parameters r3=0x101 r4=0 // This particular CB seems to be associated with sysutil itself // Then it checks for events on an event_queue associated with sysutil, checks if any cb is associated with that event and calls them with parameters that come from the event error_code cellSysutilCheckCallback(ppu_thread& ppu) { cellSysutil.trace("cellSysutilCheckCallback()"); const auto cbm = fxm::get_always<sysutil_cb_manager>(); for (auto&& func : cbm->registered.pop_all()) { if (s32 res = func(ppu)) { // Currently impossible return not_an_error(res); } if (ppu.is_stopped()) { return 0; } } return CELL_OK; }
error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout) { sys_lwmutex.trace("_sys_lwmutex_lock(lwmutex_id=0x%x, timeout=0x%llx)", lwmutex_id, timeout); const auto mutex = idm::get<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex) { if (mutex.signaled.try_dec()) { return true; } std::lock_guard lock(mutex.mutex); if (mutex.signaled.try_dec()) { return true; } mutex.sq.emplace_back(&ppu); mutex.sleep(ppu, timeout); return false; }); if (!mutex) { return CELL_ESRCH; } if (mutex.ret) { return CELL_OK; } ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (ppu.is_stopped()) { return 0; } if (timeout) { const u64 passed = get_system_time() - ppu.start_time; if (passed >= timeout) { std::lock_guard lock(mutex->mutex); if (!mutex->unqueue(mutex->sq, &ppu)) { timeout = 0; continue; } ppu.gpr[3] = CELL_ETIMEDOUT; break; } thread_ctrl::wait_for(timeout - passed); } else { thread_ctrl::wait(); } } return not_an_error(ppu.gpr[3]); }
error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout) { sys_lwcond.trace("_sys_lwcond_queue_wait(lwcond_id=0x%x, lwmutex_id=0x%x, timeout=0x%llx)", lwcond_id, lwmutex_id, timeout); std::shared_ptr<lv2_lwmutex> mutex; const auto cond = idm::get<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond) -> cpu_thread* { mutex = idm::get_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id); if (!mutex) { return nullptr; } std::lock_guard lock(cond.mutex); // Add a waiter cond.waiters++; cond.sq.emplace_back(&ppu); cond.sleep(ppu, timeout); std::lock_guard lock2(mutex->mutex); // Process lwmutex sleep queue if (const auto cpu = mutex->schedule<ppu_thread>(mutex->sq, mutex->protocol)) { return cpu; } mutex->signaled++; return nullptr; }); if (!cond || !mutex) { return CELL_ESRCH; } if (cond.ret) { cond->awake(*cond.ret); } ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (ppu.is_stopped()) { return 0; } if (timeout) { const u64 passed = get_system_time() - ppu.start_time; if (passed >= timeout) { std::lock_guard lock(cond->mutex); if (!cond->unqueue(cond->sq, &ppu)) { timeout = 0; continue; } cond->waiters--; if (mutex->signaled.try_dec()) { ppu.gpr[3] = CELL_EDEADLK; break; } ppu.gpr[3] = CELL_ETIMEDOUT; break; } thread_ctrl::wait_for(timeout - passed); } else { thread_ctrl::wait(); } } // Return cause return not_an_error(ppu.gpr[3]); }
error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout) { sys_mutex.trace("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout); const auto mutex = idm::get<lv2_obj, lv2_mutex>(mutex_id, [&](lv2_mutex& mutex) { CellError result = mutex.try_lock(ppu.id); if (result == CELL_EBUSY) { std::lock_guard lock(mutex.mutex); if (mutex.try_own(ppu, ppu.id)) { result = {}; } else { mutex.sleep(ppu, timeout); } } return result; }); if (!mutex) { return CELL_ESRCH; } if (mutex.ret) { if (mutex.ret != CELL_EBUSY) { return mutex.ret; } } else { return CELL_OK; } ppu.gpr[3] = CELL_OK; while (!ppu.state.test_and_reset(cpu_flag::signal)) { if (ppu.is_stopped()) { return 0; } if (timeout) { const u64 passed = get_system_time() - ppu.start_time; if (passed >= timeout) { std::lock_guard lock(mutex->mutex); if (!mutex->unqueue(mutex->sq, &ppu)) { timeout = 0; continue; } ppu.gpr[3] = CELL_ETIMEDOUT; break; } thread_ctrl::wait_for(timeout - passed); } else { thread_ctrl::wait(); } } return not_an_error(ppu.gpr[3]); }