s32 cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info) { sys_io.trace("cellPadPeriphGetInfo(info=*0x%x)", info); const auto handler = fxm::get<PadHandlerBase>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; const PadInfo& rinfo = handler->GetInfo(); info->max_connect = rinfo.max_connect; info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; std::vector<Pad>& pads = handler->GetPads(); // TODO: Support other types of controllers for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { if (i >= pads.size()) break; info->port_status[i] = pads[i].m_port_status; info->port_setting[i] = pads[i].m_port_setting; info->device_capability[i] = pads[i].m_device_capability; info->device_type[i] = pads[i].m_device_type; info->pclass_type[i] = CELL_PAD_PCLASS_TYPE_STANDARD; info->pclass_profile[i] = 0x0; } return CELL_OK; }
error_code sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array, s32 size, vm::ptr<u32> number) { sys_event.trace("sys_event_queue_tryreceive(equeue_id=0x%x, event_array=*0x%x, size=%d, number=*0x%x)", equeue_id, event_array, size, number); const auto queue = idm::get<lv2_obj, lv2_event_queue>(equeue_id); if (!queue) { return CELL_ESRCH; } if (queue->type != SYS_PPU_QUEUE) { return CELL_EINVAL; } semaphore_lock lock(queue->mutex); s32 count = 0; while (queue->sq.empty() && count < size && !queue->events.empty()) { auto& dest = event_array[count++]; auto event = queue->events.front(); queue->events.pop_front(); std::tie(dest.source, dest.data1, dest.data2, dest.data3) = event; } *number = count; return CELL_OK; }
s32 sys_spu_thread_group_yield(u32 id) { sys_spu.trace("sys_spu_thread_group_yield(id=0x%x)", id); LV2_LOCK; const auto group = idm::get<lv2_spu_group_t>(id); if (!group) { return CELL_ESRCH; } if (group->type & SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT) // this check may be inaccurate { return CELL_OK; } if (group->state != SPU_THREAD_GROUP_STATUS_RUNNING) { return CELL_ESTAT; } // SPU_THREAD_GROUP_STATUS_READY state is not used, so this function does nothing return CELL_OK; }
s32 cellFsClosedir(u32 fd) { cellFs.trace("cellFsClosedir(fd=0x%x)", fd); // call the syscall return sys_fs_closedir(fd); }
error_code cellVideoOutGetState(u32 videoOut, u32 deviceIndex, vm::ptr<CellVideoOutState> state) { cellSysutil.trace("cellVideoOutGetState(videoOut=%d, deviceIndex=%d, state=*0x%x)", videoOut, deviceIndex, state); if (deviceIndex) return CELL_VIDEO_OUT_ERROR_DEVICE_NOT_FOUND; switch (videoOut) { case CELL_VIDEO_OUT_PRIMARY: state->state = CELL_VIDEO_OUT_OUTPUT_STATE_ENABLED; state->colorSpace = CELL_VIDEO_OUT_COLOR_SPACE_RGB; state->displayMode.resolutionId = g_video_out_resolution_id.at(g_cfg.video.resolution); // TODO state->displayMode.scanMode = CELL_VIDEO_OUT_SCAN_MODE_PROGRESSIVE; state->displayMode.conversion = CELL_VIDEO_OUT_DISPLAY_CONVERSION_NONE; state->displayMode.aspect = g_video_out_aspect_id.at(g_cfg.video.aspect_ratio); // TODO state->displayMode.refreshRates = CELL_VIDEO_OUT_REFRESH_RATE_59_94HZ; return CELL_OK; case CELL_VIDEO_OUT_SECONDARY: *state = { CELL_VIDEO_OUT_OUTPUT_STATE_DISABLED }; // ??? return CELL_OK; } return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT; }
s32 cellFsRead(u32 fd, vm::ptr<void> buf, u64 nbytes, vm::ptr<u64> nread) { cellFs.trace("cellFsRead(fd=0x%x, buf=0x%x, nbytes=0x%llx, nread=0x%x)", fd, buf, nbytes, nread); // call the syscall return sys_fs_read(fd, buf, nbytes, nread ? nread : vm::var<u64>{}); }
ppu_error_code cellFsWriteWithOffset(u32 fd, u64 offset, vm::cptr<void> buf, u64 data_size, vm::ptr<u64> nwrite) { cellFs.trace("cellFsWriteWithOffset(fd=%d, offset=0x%llx, buf=*0x%x, data_size=0x%llx, nwrite=*0x%x)", fd, offset, buf, data_size, nwrite); if (!buf) { if (nwrite) *nwrite = 0; return CELL_EFAULT; } if (fd - 3 > 252) { if (nwrite) *nwrite = 0; return CELL_EBADF; } vm::var<lv2_file_op_rw> arg; arg->_vtable = vm::cast(0xfa8b0000); // Intentionally wrong (provide correct vtable if necessary) arg->op = 0x8000000b; arg->fd = fd; arg->buf = vm::const_ptr_cast<void>(buf); arg->offset = offset; arg->size = data_size; // Call the syscall const s32 rc = sys_fs_fcntl(fd, 0x8000000b, arg, arg.size()); // Write size written if (nwrite) *nwrite = rc && rc != CELL_EFSSPECIFIC ? 0 : arg->out_size.value(); return NOT_AN_ERROR(rc ? rc : arg->out_code.value()); }
error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo> info) { sys_io.trace("cellPadGetCapabilityInfo(port_no=%d, data_addr:=0x%x)", port_no, info.addr()); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (port_no >= CELL_MAX_PADS || !info) return CELL_PAD_ERROR_INVALID_PARAMETER; const auto& pads = handler->GetPads(); if (port_no >= pads.size() || port_no >= handler->GetInfo().max_connect) return CELL_PAD_ERROR_NO_DEVICE; const auto pad = pads[port_no]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return CELL_PAD_ERROR_NO_DEVICE; // Should return the same as device capability mask, psl1ght has it backwards in pad->h info->info[port_no] = pad->m_device_capability; return CELL_OK; }
error_code cellPadSetPortSetting(u32 port_no, u32 port_setting) { sys_io.trace("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (port_no >= CELL_MAX_PADS) return CELL_PAD_ERROR_INVALID_PARAMETER; const auto& pads = handler->GetPads(); // CELL_PAD_ERROR_NO_DEVICE is not returned in this case. // TODO: Set the setting regardless if (port_no >= pads.size() || port_no >= handler->GetInfo().max_connect) return CELL_OK; const auto pad = pads[port_no]; pad->m_port_setting = port_setting; // can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD return CELL_OK; }
error_code cellPadGetInfo(vm::ptr<CellPadInfo> info) { sys_io.trace("cellPadGetInfo(info=*0x%x)", info); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (!info) return CELL_PAD_ERROR_INVALID_PARAMETER; std::memset(info.get_ptr(), 0, sizeof(CellPadInfo)); const PadInfo& rinfo = handler->GetInfo(); info->max_connect = rinfo.max_connect; info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_MAX_PADS; ++i) { if (i >= pads.size()) break; info->status[i] = pads[i]->m_port_status; pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->product_id[i] = 0x0268; info->vendor_id[i] = 0x054C; } return CELL_OK; }
error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info) { sys_io.trace("cellPadGetInfo2(info=*0x%x)", info); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (!info) return CELL_PAD_ERROR_INVALID_PARAMETER; std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2)); const PadInfo& rinfo = handler->GetInfo(); info->max_connect = rinfo.max_connect; info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) { if (i >= pads.size()) break; info->port_status[i] = pads[i]->m_port_status; pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->port_setting[i] = pads[i]->m_port_setting; info->device_capability[i] = pads[i]->m_device_capability; info->device_type[i] = pads[i]->m_device_type; } return CELL_OK; }
error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param) { sys_io.trace("cellPadSetActDirect(port_no=%d, param=*0x%x)", port_no, param); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (port_no >= CELL_MAX_PADS || !param) return CELL_PAD_ERROR_INVALID_PARAMETER; const auto& pads = handler->GetPads(); if (port_no >= pads.size() || port_no >= handler->GetInfo().max_connect) return CELL_PAD_ERROR_NO_DEVICE; const auto pad = pads[port_no]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return CELL_PAD_ERROR_NO_DEVICE; // TODO: find out if this is checked here or later or at all if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_ACTUATOR)) return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD; // make sure reserved bits are 0. Looks like this happens after checking the pad status for (int i = 0; i < 6; i++) if (param->reserved[i]) return CELL_PAD_ERROR_INVALID_PARAMETER; handler->SetRumble(port_no, param->motor[1], param->motor[0] > 0); return CELL_OK; }
error_code cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data) { sys_io.trace("cellPadPeriphGetData(port_no=%d, data=*0x%x)", port_no, data); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; // port_no can only be 0-6 in this function if (port_no >= CELL_PAD_MAX_PORT_NUM || !data) return CELL_PAD_ERROR_INVALID_PARAMETER; const auto& pads = handler->GetPads(); if (port_no >= pads.size() || port_no >= handler->GetInfo().max_connect) return CELL_PAD_ERROR_NO_DEVICE; const auto pad = pads[port_no]; if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return CELL_PAD_ERROR_NO_DEVICE; // todo: support for 'unique' controllers, which goes in offsets 24+ in padData data->pclass_type = CELL_PAD_PCLASS_TYPE_STANDARD; data->pclass_profile = 0x0; return cellPadGetData(port_no, vm::get_addr(&data->cellpad_data)); }
s32 cellSysutilCheckCallback(ppu_thread& ppu) { cellSysutil.trace("cellSysutilCheckCallback()"); const auto cbm = fxm::get_always<sysutil_cb_manager>(); while (true) { std::lock_guard<std::mutex> lock(cbm->mutex); if (cbm->registered.empty()) { break; } const auto func = std::move(cbm->registered.front()); cbm->registered.pop(); if (s32 res = func(ppu)) { return res; } } return CELL_OK; }
s32 cellMouseGetData(u32 port_no, vm::ptr<CellMouseData> data) { sys_io.trace("cellMouseGetData(port_no=%d, data=*0x%x)", port_no, data); const auto handler = fxm::get<MouseHandlerBase>(); if (!handler) { return CELL_MOUSE_ERROR_UNINITIALIZED; } if (port_no >= handler->GetMice().size()) { return CELL_MOUSE_ERROR_NO_DEVICE; } MouseData& current_data = handler->GetData(port_no); data->update = current_data.update; data->buttons = current_data.buttons; data->x_axis = current_data.x_axis; data->y_axis = current_data.y_axis; data->wheel = current_data.wheel; data->tilt = current_data.tilt; current_data.update = CELL_MOUSE_DATA_NON; current_data.x_axis = 0; current_data.y_axis = 0; current_data.wheel = 0; return CELL_OK; }
error_code cellPadSetSensorMode(u32 port_no, u32 mode) { sys_io.trace("cellPadSetSensorMode(port_no=%d, mode=%d)", port_no, mode); const auto handler = fxm::get<pad_thread>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (port_no >= CELL_MAX_PADS) return CELL_PAD_ERROR_INVALID_PARAMETER; const auto& pads = handler->GetPads(); // CELL_PAD_ERROR_NO_DEVICE is not returned in this case. // TODO: Set the setting regardless if (port_no >= pads.size() || port_no >= handler->GetInfo().max_connect) return CELL_OK; const auto pad = pads[port_no]; // TODO: find out if this is checked here or later or at all if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE)) return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD; if (mode) pad->m_port_setting |= CELL_PAD_SETTING_SENSOR_ON; else pad->m_port_setting &= ~CELL_PAD_SETTING_SENSOR_ON; return CELL_OK; }
s32 cellFsFtruncate(u32 fd, u64 size) { cellFs.trace("cellFsFtruncate(fd=0x%x, size=0x%llx)", fd, size); // call the syscall return sys_fs_ftruncate(fd, size); }
s32 sys_io_3733EA3C(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadData> data) { // Used by the ps1 emulator built into the firmware // Seems to call the same function that getdataextra does sys_io.trace("sys_io_3733EA3C(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data); return cellPadGetDataExtra(port_no, device_type, data); }
ppu_error_code cellFsReadWithOffset(u32 fd, u64 offset, vm::ptr<void> buf, u64 buffer_size, vm::ptr<u64> nread) { cellFs.trace("cellFsReadWithOffset(fd=%d, offset=0x%llx, buf=*0x%x, buffer_size=0x%llx, nread=*0x%x)", fd, offset, buf, buffer_size, nread); if (fd - 3 > 252) { if (nread) *nread = 0; return CELL_EBADF; } vm::var<lv2_file_op_rw> arg; arg->_vtable = vm::cast(0xfa8a0000); // Intentionally wrong (provide correct vtable if necessary) arg->op = 0x8000000a; arg->fd = fd; arg->buf = buf; arg->offset = offset; arg->size = buffer_size; // Call the syscall const s32 rc = sys_fs_fcntl(fd, 0x8000000a, arg, arg.size()); // Write size read if (nread) *nread = rc && rc != CELL_EFSSPECIFIC ? 0 : arg->out_size.value(); return NOT_AN_ERROR(rc ? rc : arg->out_code.value()); }
error_code sys_memory_get_page_attribute(u32 addr, vm::ptr<sys_page_attr_t> attr) { sys_memory.trace("sys_memory_get_page_attribute(addr=0x%x, attr=*0x%x)", addr, attr); if (!vm::check_addr(addr)) { return CELL_EINVAL; } if (!vm::check_addr(attr.addr(), attr.size())) { return CELL_EFAULT; } attr->attribute = 0x40000ull; // SYS_MEMORY_PROT_READ_WRITE (TODO) attr->access_right = 0xFull; // SYS_MEMORY_ACCESS_RIGHT_ANY (TODO) if (vm::check_addr(addr, 1, vm::page_1m_size)) { attr->page_size = 0x100000; } else if (vm::check_addr(addr, 1, vm::page_64k_size)) { attr->page_size = 0x10000; } else { attr->page_size = 4096; } return CELL_OK; }
s32 cellFsWrite(u32 fd, vm::cptr<void> buf, u64 nbytes, vm::ptr<u64> nwrite) { cellFs.trace("cellFsWrite(fd=0x%x, buf=*0x%x, nbytes=0x%llx, nwrite=*0x%x)", fd, buf, nbytes, nwrite); // call the syscall return sys_fs_write(fd, buf, nbytes, nwrite ? nwrite : vm::var<u64>{}); }
error_code sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array, s32 size, vm::ptr<u32> number) { sys_event.trace("sys_event_queue_tryreceive(equeue_id=0x%x, event_array=*0x%x, size=%d, number=*0x%x)", equeue_id, event_array, size, number); const u32 count = ::narrow<u32>(size, "sys_event_queue_tryreceive"); const auto queue = idm::get<lv2_event_queue>(equeue_id); if (!queue) { return CELL_ESRCH; } if (queue->type != SYS_PPU_QUEUE) { return CELL_EINVAL; } u32 result = 0; lv2_lock lock{queue}; while (queue->waiters.empty() && result < count && queue->events.size()) { event_array[result++].tie() = queue->events.front(); queue->events.pop_front(); } *number = result; return CELL_OK; }
s32 cellFsFstat(u32 fd, vm::ptr<CellFsStat> sb) { cellFs.trace("cellFsFstat(fd=0x%x, sb=*0x%x)", fd, sb); // call the syscall return sys_fs_fstat(fd, sb); }
error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3) { sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3); const auto port = idm::get<lv2_event_port>(eport_id, [](lv2_event_port& port) { lv2_lock lock{port}; return port.queue.lock(); }); if (!port) { return CELL_ESRCH; } if (!port.value) { return CELL_ENOTCONN; } if (!port.value->push(port->name ? port->name : (u64)process_getpid() << 32 | eport_id, data1, data2, data3)) { return CELL_EBUSY; } return CELL_OK; }
error_code sys_lwmutex_destroy(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex) { sysPrxForUser.trace("sys_lwmutex_destroy(lwmutex=*0x%x)", lwmutex); if (g_cfg.core.hle_lwmutex) { return sys_mutex_destroy(lwmutex->sleep_queue); } // check to prevent recursive locking in the next call if (lwmutex->vars.owner.load() == ppu.id) { return CELL_EBUSY; } // attempt to lock the mutex if (error_code res = sys_lwmutex_trylock(ppu, lwmutex)) { return res; } // call the syscall if (error_code res = _sys_lwmutex_destroy(lwmutex->sleep_queue)) { // unlock the mutex if failed sys_lwmutex_unlock(ppu, lwmutex); return res; } // deleting succeeded lwmutex->vars.owner.release(lwmutex_dead); return CELL_OK; }
s32 sys_timer_sleep(u32 sleep_time) { sys_timer.trace("sys_timer_sleep(sleep_time=%d)", sleep_time); const u64 start_time = get_system_time(); const u64 useconds = sleep_time * 1000000ull; u64 passed; while (useconds > (passed = get_system_time() - start_time) + 1000) { CHECK_EMU_STATUS; std::this_thread::sleep_for(1ms); } if (useconds > passed) { std::this_thread::sleep_for(std::chrono::microseconds(useconds - passed)); } CHECK_EMU_STATUS; return CELL_OK; }
s32 cellSslCertificateLoader(u64 flag, vm::ptr<char> buffer, u32 size, vm::ptr<u32> required) { cellSsl.trace("cellSslCertificateLoader(flag=%llu, buffer=0x%x, size=%zu, required=0x%x)", flag, buffer, size, required); const std::bitset<58> flagBits(flag); const std::string certPath = vfs::get("/dev_flash/") + "data/cert/"; if (required) { *required = 0; for (int i = 1; i <= flagBits.size(); i++) { if (!flagBits[i-1]) continue; // If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs. *required += (u32)(getCert(certPath, i, flagBits[BaltimoreCert-1]).size()); } } else { std::string final; for (int i = 1; i <= flagBits.size(); i++) { if (!flagBits[i-1]) continue; // If we're loading cert 6 (the baltimore cert), then we need set that we're loading the 'normal' set of certs. final.append(getCert(certPath, i, flagBits[BaltimoreCert-1])); } memset(buffer.get_ptr(), '\0', size - 1); memcpy(buffer.get_ptr(), final.c_str(), final.size()); }
error_code cellKbRead(u32 port_no, vm::ptr<CellKbData> data) { sys_io.trace("cellKbRead(port_no=%d, data=*0x%x)", port_no, data); const auto handler = fxm::get<KeyboardHandlerBase>(); if (!handler) return CELL_KB_ERROR_UNINITIALIZED; const std::vector<Keyboard>& keyboards = handler->GetKeyboards(); if (port_no >= keyboards.size()) return CELL_KB_ERROR_INVALID_PARAMETER; KbData& current_data = handler->GetData(port_no); data->led = current_data.led; data->mkey = current_data.mkey; data->len = std::min((u32)current_data.len, CELL_KB_MAX_KEYCODES); for (s32 i=0; i<current_data.len; i++) { data->keycode[i] = current_data.keycode[i]; } current_data.len = 0; return CELL_OK; }
s32 sys_spu_thread_write_snr(u32 id, u32 number, u32 value) { sys_spu.trace("sys_spu_thread_write_snr(id=0x%x, number=%d, value=0x%x)", id, number, value); LV2_LOCK; const auto thread = idm::get<SPUThread>(id); if (!thread) { return CELL_ESRCH; } if (number > 1) { return CELL_EINVAL; } const auto group = thread->tg.lock(); if (!group) { throw EXCEPTION("Invalid SPU thread group"); } //if (group->state < SPU_THREAD_GROUP_STATUS_WAITING || group->state > SPU_THREAD_GROUP_STATUS_RUNNING) // ??? //{ // return CELL_ESTAT; //} thread->push_snr(number, value); return CELL_OK; }
s32 cellPadSetSensorMode(u32 port_no, u32 mode) { sys_io.trace("cellPadSetSensorMode(port_no=%d, mode=%d)", port_no, mode); const auto handler = fxm::get<PadHandlerBase>(); if (!handler) return CELL_PAD_ERROR_UNINITIALIZED; if (mode != 0 && mode != 1) return CELL_PAD_ERROR_INVALID_PARAMETER; const PadInfo& rinfo = handler->GetInfo(); if (port_no >= rinfo.max_connect) return CELL_PAD_ERROR_INVALID_PARAMETER; if (port_no >= rinfo.now_connect) return CELL_PAD_ERROR_NO_DEVICE; std::vector<Pad>& pads = handler->GetPads(); if (mode) pads[port_no].m_port_setting |= CELL_PAD_SETTING_SENSOR_ON; else pads[port_no].m_port_setting &= ~CELL_PAD_SETTING_SENSOR_ON; return CELL_OK; }