Merge pull request #376 from wheremyfoodat/scheduler

More camera emulation
This commit is contained in:
wheremyfoodat 2024-01-23 15:01:59 +00:00 committed by GitHub
commit dcdafda658
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 186 additions and 15 deletions

View file

@ -12,22 +12,39 @@
class Kernel;
class CAMService {
using Event = std::optional<Handle>;
struct Port {
Event bufferErrorInterruptevent = std::nullopt;
u16 transferBytes;
void reset() {
bufferErrorInterruptevent = std::nullopt;
transferBytes = 256;
}
};
Handle handle = KernelHandles::CAM;
Memory& mem;
Kernel& kernel;
MAKE_LOG_FUNCTION(log, camLogger)
using Event = std::optional<Handle>;
static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH
std::array<Event, portCount> bufferErrorInterruptEvents;
static constexpr size_t portCount = 2;
std::array<Port, portCount> ports;
// Service commands
void driverInitialize(u32 messagePointer);
void driverFinalize(u32 messagePointer);
void getMaxLines(u32 messagePointer);
void getBufferErrorInterruptEvent(u32 messagePointer);
void getSuitableY2RCoefficients(u32 messagePointer);
void getTransferBytes(u32 messagePointer);
void setContrast(u32 messagePointer);
void setFrameRate(u32 messagePointer);
void setSize(u32 messagePointer);
void setTransferLines(u32 messagePointer);
void setTrimming(u32 messagePointer);
void setTrimmingParamsCenter(u32 messagePointer);
public:
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}

View file

@ -61,6 +61,7 @@ class FSService {
void getFreeBytes(u32 messagePointer);
void getFormatInfo(u32 messagePointer);
void getPriority(u32 messagePointer);
void getSdmcArchiveResource(u32 messagePointer);
void getThisSaveDataSecureValue(u32 messagePointer);
void theGameboyVCFunction(u32 messagePointer);
void initialize(u32 messagePointer);

View file

@ -6,24 +6,78 @@ namespace CAMCommands {
enum : u32 {
GetBufferErrorInterruptEvent = 0x00060040,
DriverInitialize = 0x00390000,
DriverFinalize = 0x003A0000,
SetTransferLines = 0x00090100,
GetMaxLines = 0x000A0080,
GetTransferBytes = 0x000C0040,
SetTrimming = 0x000E0080,
SetTrimmingParamsCenter = 0x00120140,
SetSize = 0x001F00C0, // Set size has different headers between cam:u and New3DS QTM module
SetFrameRate = 0x00200080,
SetContrast = 0x00230080,
GetSuitableY2rStandardCoefficient = 0x00360000,
};
}
void CAMService::reset() { bufferErrorInterruptEvents.fill(std::nullopt); }
// Helper struct for working with camera ports
class PortSelect {
u32 value;
public:
PortSelect(u32 val) : value(val) {}
bool isValid() const { return value < 4; }
bool isSinglePort() const {
// 1 corresponds to the first camera port and 2 corresponds to the second port
return value == 1 || value == 2;
}
bool isBothPorts() const {
// 3 corresponds to both ports
return value == 3;
}
// Returns the index of the camera port, assuming that it's only a single port
int getSingleIndex() const {
if (!isSinglePort()) [[unlikely]] {
Helpers::panic("Camera: getSingleIndex called for port with invalid value");
}
return value - 1;
}
std::vector<int> getPortIndices() const {
switch (value) {
case 1: return {0}; // Only port 1
case 2: return {1}; // Only port 2
case 3: return {0, 1}; // Both port 1 and port 2
default: return {}; // No ports or invalid ports
}
}
};
void CAMService::reset() {
for (auto& port : ports) {
port.reset();
}
}
void CAMService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case CAMCommands::DriverInitialize: driverInitialize(messagePointer); break;
case CAMCommands::DriverFinalize: driverFinalize(messagePointer); break;
case CAMCommands::GetBufferErrorInterruptEvent: getBufferErrorInterruptEvent(messagePointer); break;
case CAMCommands::GetMaxLines: getMaxLines(messagePointer); break;
case CAMCommands::GetSuitableY2rStandardCoefficient: getSuitableY2RCoefficients(messagePointer); break;
case CAMCommands::GetTransferBytes: getTransferBytes(messagePointer); break;
case CAMCommands::SetContrast: setContrast(messagePointer); break;
case CAMCommands::SetFrameRate: setFrameRate(messagePointer); break;
case CAMCommands::SetTransferLines: setTransferLines(messagePointer); break;
case CAMCommands::SetTrimming: setTrimming(messagePointer); break;
case CAMCommands::SetTrimmingParamsCenter: setTrimmingParamsCenter(messagePointer); break;
case CAMCommands::SetSize: setSize(messagePointer); break;
default:
Helpers::panic("Unimplemented CAM service requested. Command: %08X\n", command);
break;
@ -36,6 +90,12 @@ void CAMService::driverInitialize(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
}
void CAMService::driverFinalize(u32 messagePointer) {
log("CAM::DriverFinalize\n");
mem.write32(messagePointer, IPC::responseHeader(0x3A, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void CAMService::setContrast(u32 messagePointer) {
const u32 cameraSelect = mem.read32(messagePointer + 4);
const u32 contrast = mem.read32(messagePointer + 8);
@ -47,12 +107,23 @@ void CAMService::setContrast(u32 messagePointer) {
}
void CAMService::setTransferLines(u32 messagePointer) {
const u32 port = mem.read32(messagePointer + 4);
const s16 lines = mem.read16(messagePointer + 8);
const s16 width = mem.read16(messagePointer + 12);
const s16 height = mem.read16(messagePointer + 16);
const u32 portIndex = mem.read32(messagePointer + 4);
const u16 lines = mem.read16(messagePointer + 8);
const u16 width = mem.read16(messagePointer + 12);
const u16 height = mem.read16(messagePointer + 16);
const PortSelect port(portIndex);
log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", port, lines, width, height);
if (port.isValid()) {
const u32 transferBytes = lines * width * 2;
for (int i : port.getPortIndices()) {
ports[i].transferBytes = transferBytes;
}
} else {
Helpers::warn("CAM::SetTransferLines: Invalid port\n");
}
log("CAM::SetTransferLines (port = %d, lines = %d, width = %d, height = %d)\n", portIndex, lines, width, height);
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
@ -68,6 +139,41 @@ void CAMService::setFrameRate(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
}
void CAMService::setSize(u32 messagePointer) {
const u32 cameraSelect = mem.read32(messagePointer + 4);
const u32 size = mem.read32(messagePointer + 8);
const u32 context = mem.read32(messagePointer + 12);
log("CAM::SetSize (camera select = %d, size = %d, context = %d)\n", cameraSelect, size, context);
mem.write32(messagePointer, IPC::responseHeader(0x1F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void CAMService::setTrimming(u32 messagePointer) {
const u32 port = mem.read32(messagePointer + 4);
const bool trim = mem.read8(messagePointer + 8) != 0;
log("CAM::SetTrimming (port = %d, trimming = %s)\n", port, trim ? "enabled" : "disabled");
mem.write32(messagePointer, IPC::responseHeader(0x0E, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void CAMService::setTrimmingParamsCenter(u32 messagePointer) {
const u32 port = mem.read32(messagePointer + 4);
const s16 trimWidth = s16(mem.read16(messagePointer + 8));
const s16 trimHeight = s16(mem.read16(messagePointer + 12));
const s16 cameraWidth = s16(mem.read16(messagePointer + 16));
const s16 cameraHeight = s16(mem.read16(messagePointer + 20));
log("CAM::SetTrimmingParamsCenter (port = %d), trim size = (%d, %d), camera size = (%d, %d)\n", port, trimWidth, trimHeight, cameraWidth,
cameraHeight);
mem.write32(messagePointer, IPC::responseHeader(0x12, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
// Algorithm taken from Citra
// https://github.com/citra-emu/citra/blob/master/src/core/hle/service/cam/cam.cpp#L465
void CAMService::getMaxLines(u32 messagePointer) {
@ -100,16 +206,40 @@ void CAMService::getMaxLines(u32 messagePointer) {
}
}
void CAMService::getSuitableY2RCoefficients(u32 messagePointer) {
log("CAM::GetSuitableY2RCoefficients\n");
mem.write32(messagePointer, IPC::responseHeader(0x36, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
// Y2R standard coefficient value
mem.write32(messagePointer + 8, 0);
}
void CAMService::getTransferBytes(u32 messagePointer) {
const u32 portIndex = mem.read32(messagePointer + 4);
const PortSelect port(portIndex);
log("CAM::GetTransferBytes (port = %d)\n", portIndex);
mem.write32(messagePointer, IPC::responseHeader(0x0C, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
if (port.isSinglePort()) {
mem.write32(messagePointer + 8, ports[port.getSingleIndex()].transferBytes);
} else {
// TODO: This should return the proper error code
Helpers::warn("CAM::GetTransferBytes: Invalid port index");
mem.write32(messagePointer + 8, 0);
}
}
void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
const u32 port = mem.read32(messagePointer + 4);
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", port);
const u32 portIndex = mem.read32(messagePointer + 4);
const PortSelect port(portIndex);
log("CAM::GetBufferErrorInterruptEvent (port = %d)\n", portIndex);
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 2));
if (port >= portCount) {
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
} else {
auto& event = bufferErrorInterruptEvents[port];
if (port.isSinglePort()) {
auto& event = ports[port.getSingleIndex()].bufferErrorInterruptevent;
if (!event.has_value()) {
event = kernel.makeEvent(ResetType::OneShot);
}
@ -117,5 +247,7 @@ void CAMService::getBufferErrorInterruptEvent(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0);
mem.write32(messagePointer + 12, event.value());
} else {
Helpers::panic("CAM::GetBufferErrorInterruptEvent: Invalid port");
}
}

View file

@ -27,6 +27,7 @@ namespace FSCommands {
CloseArchive = 0x080E0080,
FormatThisUserSaveData = 0x080F0180,
GetFreeBytes = 0x08120080,
GetSdmcArchiveResource = 0x08140000,
IsSdmcDetected = 0x08170000,
IsSdmcWritable = 0x08180000,
CardSlotIsInserted = 0x08210000,
@ -179,6 +180,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
case FSCommands::GetFreeBytes: getFreeBytes(messagePointer); break;
case FSCommands::GetFormatInfo: getFormatInfo(messagePointer); break;
case FSCommands::GetPriority: getPriority(messagePointer); break;
case FSCommands::GetSdmcArchiveResource: getSdmcArchiveResource(messagePointer); break;
case FSCommands::GetThisSaveDataSecureValue: getThisSaveDataSecureValue(messagePointer); break;
case FSCommands::Initialize: initialize(messagePointer); break;
case FSCommands::InitializeWithSdkVersion: initializeWithSdkVersion(messagePointer); break;
@ -764,3 +766,22 @@ void FSService::renameFile(u32 messagePointer) {
const HorizonResult res = sourceArchive->archive->renameFile(sourcePath, destPath);
mem.write32(messagePointer + 4, static_cast<u32>(res));
}
void FSService::getSdmcArchiveResource(u32 messagePointer) {
log("FS::GetSdmcArchiveResource"); // For the time being, return the same stubbed archive resource for every media type
static constexpr ArchiveResource resource = {
.sectorSize = 512,
.clusterSize = 16_KB,
.partitionCapacityInClusters = 0x80000, // 0x80000 * 16 KB = 8GB
.freeSpaceInClusters = 0x80000, // Same here
};
mem.write32(messagePointer, IPC::responseHeader(0x814, 5, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, resource.sectorSize);
mem.write32(messagePointer + 12, resource.clusterSize);
mem.write32(messagePointer + 16, resource.partitionCapacityInClusters);
mem.write32(messagePointer + 20, resource.freeSpaceInClusters);
}