static int __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { bool needsResched = false; bool needsWait = false; int result = __KernelReceiveMsgPipe(m, receiveBufAddr, receiveSize, waitMode, resultAddr, timeoutPtr, cbEnabled, poll, needsResched, needsWait); if (needsResched) hleReSchedule(cbEnabled, "msgpipe data received"); if (needsWait) { if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, m->GetUID(), MSGPIPE_WAIT_VALUE_RECV, timeoutPtr, cbEnabled, "msgpipe receive waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; } return result; }
static int __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { hleEatCycles(2400); bool needsResched = false; bool needsWait = false; int result = __KernelSendMsgPipe(m, sendBufAddr, sendSize, waitMode, resultAddr, timeoutPtr, cbEnabled, poll, needsResched, needsWait); if (needsResched) hleReSchedule(cbEnabled, "msgpipe data sent"); if (needsWait) { if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, m->GetUID(), MSGPIPE_WAIT_VALUE_SEND, timeoutPtr, cbEnabled, "msgpipe send waited"); else result = SCE_KERNEL_ERROR_WAIT_TIMEOUT; } return result; }
int __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { u32 curReceiveAddr = receiveBufAddr; SceUID uid = m->GetUID(); // MsgPipe buffer size is 0, receiving directly from waiting send threads if (m->nmp.bufSize == 0) { m->SortSendThreads(); // While they're still sending waiting threads (which can send data) while (!m->sendWaitingThreads.empty() && receiveSize != 0) { MsgPipeWaitingThread *thread = &m->sendWaitingThreads.front(); // For send threads, "freeSize" is "free to be read". u32 bytesToReceive = std::min(thread->freeSize, receiveSize); if (bytesToReceive > 0) { thread->ReadBuffer(Memory::GetPointer(curReceiveAddr), bytesToReceive); receiveSize -= bytesToReceive; curReceiveAddr += bytesToReceive; if (thread->freeSize == 0 || thread->waitMode == SCE_KERNEL_MPW_ASAP) { thread->Complete(uid, 0); m->sendWaitingThreads.erase(m->sendWaitingThreads.begin()); hleReSchedule(cbEnabled, "msgpipe data received"); thread = NULL; } } } // All data hasn't been received and (mode isn't ASAP or nothing was received) if (receiveSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curReceiveAddr == receiveBufAddr)) { if (poll) { // Generally, result is not updated in this case. But for a 0 size buffer in ASAP mode, it is. if (Memory::IsValidAddress(resultAddr) && waitMode == SCE_KERNEL_MPW_ASAP) Memory::Write_U32(curReceiveAddr - receiveBufAddr, resultAddr); return SCE_KERNEL_ERROR_MPP_EMPTY; } else { m->AddReceiveWaitingThread(__KernelGetCurThread(), curReceiveAddr, receiveSize, waitMode, resultAddr); if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, uid, 0, timeoutPtr, cbEnabled, "msgpipe receive waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; return 0; } } } // Getting data from the MsgPipe buffer else { if (receiveSize > (u32) m->nmp.bufSize) { ERROR_LOG(HLE, "__KernelReceiveMsgPipe(%d): size %d too large for buffer", uid, receiveSize); return SCE_KERNEL_ERROR_ILLEGAL_SIZE; } while (m->GetUsedSize() > 0) { u32 bytesToReceive = std::min(receiveSize, m->GetUsedSize()); if (bytesToReceive != 0) { Memory::Memcpy(curReceiveAddr, Memory::GetPointer(m->buffer), bytesToReceive); m->nmp.freeSize += bytesToReceive; memmove(Memory::GetPointer(m->buffer), Memory::GetPointer(m->buffer) + bytesToReceive, m->GetUsedSize()); curReceiveAddr += bytesToReceive; receiveSize -= bytesToReceive; m->CheckSendThreads(); } else break; } if (receiveSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curReceiveAddr == receiveBufAddr)) { if (poll) return SCE_KERNEL_ERROR_MPP_EMPTY; else { m->AddReceiveWaitingThread(__KernelGetCurThread(), curReceiveAddr, receiveSize, waitMode, resultAddr); if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, uid, 0, timeoutPtr, cbEnabled, "msgpipe receive waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; return 0; } } } if (Memory::IsValidAddress(resultAddr)) Memory::Write_U32(curReceiveAddr - receiveBufAddr, resultAddr); return 0; }
int __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll) { u32 curSendAddr = sendBufAddr; SceUID uid = m->GetUID(); // If the buffer size is 0, nothing is buffered and all operations wait. if (m->nmp.bufSize == 0) { m->SortReceiveThreads(); while (!m->receiveWaitingThreads.empty() && sendSize != 0) { MsgPipeWaitingThread *thread = &m->receiveWaitingThreads.front(); u32 bytesToSend = std::min(thread->freeSize, sendSize); if (bytesToSend > 0) { thread->WriteBuffer(Memory::GetPointer(curSendAddr), bytesToSend); sendSize -= bytesToSend; curSendAddr += bytesToSend; if (thread->freeSize == 0 || thread->waitMode == SCE_KERNEL_MPW_ASAP) { thread->Complete(uid, 0); m->receiveWaitingThreads.erase(m->receiveWaitingThreads.begin()); hleReSchedule(cbEnabled, "msgpipe data sent"); thread = NULL; } } } // If there is still data to send and (we want to send all of it or we didn't send anything) if (sendSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curSendAddr == sendBufAddr)) { if (poll) { // Generally, result is not updated in this case. But for a 0 size buffer in ASAP mode, it is. if (Memory::IsValidAddress(resultAddr) && waitMode == SCE_KERNEL_MPW_ASAP) Memory::Write_U32(curSendAddr - sendBufAddr, resultAddr); return SCE_KERNEL_ERROR_MPP_FULL; } else { m->AddSendWaitingThread(__KernelGetCurThread(), curSendAddr, sendSize, waitMode, resultAddr); if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, uid, 0, timeoutPtr, cbEnabled, "msgpipe send waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; return 0; } } } else { if (sendSize > (u32) m->nmp.bufSize) { ERROR_LOG(HLE, "__KernelSendMsgPipe(%d): size %d too large for buffer", uid, sendSize); return SCE_KERNEL_ERROR_ILLEGAL_SIZE; } u32 bytesToSend = 0; // If others are already waiting, space or not, we have to get in line. m->SortSendThreads(); if (m->sendWaitingThreads.empty()) { if (sendSize <= (u32) m->nmp.freeSize) bytesToSend = sendSize; else if (waitMode == SCE_KERNEL_MPW_ASAP) bytesToSend = m->nmp.freeSize; } if (bytesToSend != 0) { Memory::Memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), Memory::GetPointer(sendBufAddr), bytesToSend); m->nmp.freeSize -= bytesToSend; curSendAddr += bytesToSend; sendSize -= bytesToSend; if (m->CheckReceiveThreads()) hleReSchedule(cbEnabled, "msgpipe data sent"); } else if (sendSize != 0) { if (poll) return SCE_KERNEL_ERROR_MPP_FULL; else { m->AddSendWaitingThread(__KernelGetCurThread(), curSendAddr, sendSize, waitMode, resultAddr); if (__KernelSetMsgPipeTimeout(timeoutPtr)) __KernelWaitCurThread(WAITTYPE_MSGPIPE, uid, 0, timeoutPtr, cbEnabled, "msgpipe send waited"); else return SCE_KERNEL_ERROR_WAIT_TIMEOUT; return 0; } } } // We didn't wait, so update the number of bytes transferred now. if (Memory::IsValidAddress(resultAddr)) Memory::Write_U32(curSendAddr - sendBufAddr, resultAddr); return 0; }