ajm: rewrite using new ioctl handling api
Some checks failed
Formatting check / formatting-check (push) Has been cancelled
Build RPCSX / build-linux (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.1-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.2-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.4-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv8.5-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv9-a) (push) Has been cancelled
Build RPCSX / build-android (arm64-v8a, armv9.1-a) (push) Has been cancelled
Build RPCSX / build-android (x86_64, x86-64) (push) Has been cancelled

This commit is contained in:
DH 2025-10-11 20:22:03 +03:00
parent 8cfb4e8d16
commit e27926d629

View file

@ -1,6 +1,7 @@
#include "ajm.hpp" #include "ajm.hpp"
#include "io-device.hpp" #include "io-device.hpp"
#include "orbis-config.hpp" #include "orbis-config.hpp"
#include "orbis/IoDevice.hpp"
#include "orbis/KernelAllocator.hpp" #include "orbis/KernelAllocator.hpp"
#include "orbis/file.hpp" #include "orbis/file.hpp"
#include "orbis/thread/Thread.hpp" #include "orbis/thread/Thread.hpp"
@ -40,7 +41,20 @@ enum {
AJM_RESULT_CODEC_ERROR = 0x40000000, AJM_RESULT_CODEC_ERROR = 0x40000000,
}; };
struct AjmDevice : orbis::IoDevice { enum {
AJM_IOCTL_FINALIZE = 0xc0288900,
AJM_IOCTL_MODULE_REGISTER = 0xc0288903,
AJM_IOCTL_MODULE_UNREGISTER = 0xc0288904,
AJM_IOCTL_INSTANCE_CREATE = 0xc0288905,
AJM_IOCTL_INSTANCE_DESTROY = 0xc0288906,
AJM_IOCTL_START_BATCH_BUFFER = 0xc0288907,
AJM_IOCTL_WAIT_BATCH_BUFFER = 0xc0288908,
AJM_IOCTL_INSTANCE_EXTEND = 0xc028890a,
AJM_IOCTL_INSTANCE_SWITCH = 0xc028890b,
};
struct AjmDevice
: orbis::IoDeviceWithIoctl<orbis::ioctl::group(AJM_IOCTL_FINALIZE)> {
rx::shared_mutex mtx; rx::shared_mutex mtx;
orbis::uint32_t batchId = 1; // temp orbis::uint32_t batchId = 1; // temp
@ -50,6 +64,8 @@ struct AjmDevice : orbis::IoDevice {
orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,
orbis::Thread *thread) override; orbis::Thread *thread) override;
AjmDevice();
}; };
AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) { AVSampleFormat ajmToAvFormat(AJMFormat ajmFormat) {
@ -105,70 +121,83 @@ void resetAt9(Instance *instance) {
} }
} }
static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request, struct AjmIoctlInstanceFinalize {
void *argp, orbis::Thread *thread) {
auto device = static_cast<AjmDevice *>(file->device.get());
// 0xc0288900 - finalize
// 0xc0288903 - module register
// 0xc0288904 - module unregister
// 0xc0288905 - instance create
// 0xc0288906 - instance destroy
// 0xc028890a - instance extend
// 0xc028890b - intasnce switch
// 0xc0288907 - start batch buffer
// 0xc0288908 - wait batch buffer
// 0xc0288900 - unregister context
if (request == 0xc0288906) {
struct InstanceDestroyArgs {
orbis::uint32_t result; orbis::uint32_t result;
orbis::uint32_t unk0; orbis::uint32_t unk0;
orbis::uint32_t instanceId; orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
}; };
auto args = reinterpret_cast<InstanceDestroyArgs *>(argp);
if (device->instanceMap.erase(args->instanceId) == 0) {
return orbis::ErrorCode::INVAL;
}
args->result = 0; static orbis::ErrorCode ajm_ioctl_finalize(orbis::Thread *, AjmDevice *device,
AjmIoctlInstanceFinalize &args) {
ORBIS_LOG_ERROR(__FUNCTION__, args.instanceId);
args.result = 0;
args.unk0 = 0;
return {}; return {};
} }
if (request == 0xc0288903 || request == 0xc0288904 || request == 0xc0288900) { struct AjmIoctlModuleRegister {
auto arg = reinterpret_cast<std::uint32_t *>(argp)[2]; orbis::uint32_t result;
ORBIS_LOG_ERROR(__FUNCTION__, request, arg); orbis::uint32_t unk0;
*reinterpret_cast<std::uint64_t *>(argp) = 0; orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
};
static orbis::ErrorCode
ajm_ioctl_module_register(orbis::Thread *, AjmDevice *device,
AjmIoctlModuleRegister &args) {
ORBIS_LOG_ERROR(__FUNCTION__, args.instanceId);
args.result = 0;
args.unk0 = 0;
return {}; return {};
} }
if (request == 0xc0288905) { struct AjmIoctlModuleUnregister {
struct InstanceCreateArgs { orbis::uint32_t result;
orbis::uint32_t unk0;
orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
};
static orbis::ErrorCode
ajm_ioctl_module_unregister(orbis::Thread *, AjmDevice *device,
AjmIoctlModuleUnregister &args) {
ORBIS_LOG_ERROR(__FUNCTION__, args.instanceId);
args.result = 0;
args.unk0 = 0;
return {};
}
struct AjmIoctlInstanceCreate {
orbis::uint32_t result; orbis::uint32_t result;
orbis::uint32_t unk0; orbis::uint32_t unk0;
orbis::uint64_t flags; orbis::uint64_t flags;
orbis::uint32_t codec; orbis::uint32_t codec;
orbis::uint32_t instanceId; orbis::uint32_t instanceId;
orbis::uint32_t unk[4];
}; };
static orbis::ErrorCode
ajm_ioctl_instance_create(orbis::Thread *, AjmDevice *device,
AjmIoctlInstanceCreate &args) {
ORBIS_LOG_WARNING(__FUNCTION__, args.flags, args.codec);
std::lock_guard lock(device->mtx);
auto args = reinterpret_cast<InstanceCreateArgs *>(argp); auto codecId = AJMCodecs(args.codec);
auto codecId = AJMCodecs(args->codec);
auto codecOffset = codecId << 0xe; auto codecOffset = codecId << 0xe;
args.result = 0;
if (codecId < AJM_CODEC_COUNT) { if (codecId < AJM_CODEC_COUNT) {
args->result = 0; args.instanceId = codecOffset + device->instanceIds[codecId]++;
args->instanceId = codecOffset + device->instanceIds[codecId]++;
auto [it, inserted] = device->instanceMap.try_emplace(args->instanceId); auto [it, inserted] = device->instanceMap.try_emplace(args.instanceId);
assert(inserted); assert(inserted);
auto &instance = it->second; auto &instance = it->second;
instance.codec = codecId; instance.codec = codecId;
instance.maxChannels = instance.maxChannels =
AJMChannels(((args->flags & ~7) & (0xFF & ~0b11)) >> 3); AJMChannels(((args.flags & ~7) & (0xFF & ~0b11)) >> 3);
instance.outputFormat = AJMFormat((args->flags & ~7) & 0b11); instance.outputFormat = AJMFormat((args.flags & ~7) & 0b11);
if (codecId == AJM_CODEC_AAC) { if (codecId == AJM_CODEC_AAC) {
instance.aac.isHeaac = ((args->flags & ~7) & 0x100000000) != 0; instance.aac.isHeaac = ((args.flags & ~7) & 0x100000000) != 0;
instance.aac.isFrameSkipEnabled = instance.aac.isFrameSkipEnabled = ((args.flags & ~7) & 0x200000000) == 0;
((args->flags & ~7) & 0x200000000) == 0;
} }
if (codecId == AJM_CODEC_At9) { if (codecId == AJM_CODEC_At9) {
instance.at9.handle = Atrac9GetHandle(); instance.at9.handle = Atrac9GetHandle();
@ -207,41 +236,91 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
instance.codecCtx = codecCtx; instance.codecCtx = codecCtx;
} }
ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, ORBIS_LOG_ERROR(__FUNCTION__, args.result, args.unk0, args.flags,
args->flags, args->codec, args->instanceId, args.codec, args.instanceId,
(std::uint16_t)instance.outputFormat, (std::uint16_t)instance.outputFormat,
(std::uint16_t)instance.maxChannels); (std::uint16_t)instance.maxChannels);
} else { } else {
args->instanceId = codecOffset + device->unimplementedInstanceId++; args.instanceId = codecOffset + device->unimplementedInstanceId++;
ORBIS_LOG_ERROR(__FUNCTION__, request, "unimplemented codec", ORBIS_LOG_ERROR(__FUNCTION__, "unimplemented codec", args.result, args.unk0,
args->result, args->unk0, args->flags, args->codec, args.flags, args.codec, args.instanceId);
args->instanceId);
} }
return {}; return {};
} }
if (request == 0xc0288907) { struct AjmIoctlInstanceDestroy {
struct StartBatchBufferArgs {
orbis::uint32_t result; orbis::uint32_t result;
orbis::uint32_t unk0; orbis::uint32_t unk0;
std::byte *pBatch; orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
};
static orbis::ErrorCode
ajm_ioctl_instance_destroy(orbis::Thread *, AjmDevice *device,
AjmIoctlInstanceDestroy &args) {
ORBIS_LOG_ERROR(__FUNCTION__, args.instanceId);
std::lock_guard lock(device->mtx);
if (device->instanceMap.erase(args.instanceId) == 0) {
return orbis::ErrorCode::INVAL;
}
args.result = 0;
return {};
}
struct AjmIoctlInstanceExtend {
orbis::uint32_t result;
orbis::uint32_t unk0;
orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
};
static orbis::ErrorCode
ajm_ioctl_instance_extend(orbis::Thread *thread, AjmDevice *device,
AjmIoctlInstanceExtend &args) {
ORBIS_LOG_ERROR(__FUNCTION__);
thread->where();
args.result = 0;
args.unk0 = 0;
return {};
}
struct AjmIoctlInstanceSwitch {
orbis::uint32_t result;
orbis::uint32_t unk0;
orbis::uint32_t instanceId;
orbis::uint32_t unk[7];
};
static orbis::ErrorCode
ajm_ioctl_instance_switch(orbis::Thread *thread, AjmDevice *device,
AjmIoctlInstanceSwitch &args) {
ORBIS_LOG_ERROR(__FUNCTION__);
thread->where();
args.result = 0;
args.unk0 = 0;
return {};
}
struct AjmIoctlStartBatchBuffer {
orbis::uint32_t result;
orbis::uint32_t unk0;
orbis::ptr<std::byte> pBatch;
orbis::uint32_t batchSize; orbis::uint32_t batchSize;
orbis::uint32_t priority; orbis::uint32_t priority;
orbis::uint64_t batchError; orbis::uint64_t batchError;
orbis::uint32_t batchId; orbis::uint32_t batchId;
}; };
auto args = reinterpret_cast<StartBatchBufferArgs *>(argp); static orbis::ErrorCode
args->result = 0; ajm_ioctl_start_batch_buffer(orbis::Thread *, AjmDevice *device,
args->batchId = device->batchId++; AjmIoctlStartBatchBuffer &args) {
// ORBIS_LOG_ERROR(__FUNCTION__, request, args->result, args->unk0, args.result = 0;
// args->pBatch, args->batchSize, args->priority, args.batchId = device->batchId++;
// args->batchError, args->batchId); // ORBIS_LOG_ERROR(__FUNCTION__, args.result, args.unk0, args.pBatch,
// args.batchSize, args.priority, args.batchError, args.batchId);
// thread->where(); // thread->where();
auto ptr = args->pBatch; auto ptr = args.pBatch;
auto endPtr = args->pBatch + args->batchSize; auto endPtr = args.pBatch + args.batchSize;
while (ptr < endPtr) { while (ptr < endPtr) {
auto header = (InstructionHeader *)ptr; auto header = (InstructionHeader *)ptr;
@ -271,7 +350,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
reinterpret_cast<AJMSidebandResult *>(ctrl->pSidebandOutput); reinterpret_cast<AJMSidebandResult *>(ctrl->pSidebandOutput);
*result = {}; *result = {};
ORBIS_LOG_ERROR(__FUNCTION__, request, "control buffer", ctrl->opcode, ORBIS_LOG_ERROR(__FUNCTION__, "control buffer", ctrl->opcode,
ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo, ctrl->commandId, ctrl->flagsHi, ctrl->flagsLo,
ctrl->sidebandInputSize, ctrl->sidebandOutputSize); ctrl->sidebandInputSize, ctrl->sidebandOutputSize);
if (ctrl->getFlags() & CONTROL_RESET) { if (ctrl->getFlags() & CONTROL_RESET) {
@ -301,8 +380,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
? maxChannels ? maxChannels
: instance.at9.inputChannels; : instance.at9.inputChannels;
// TODO: check max channels // TODO: check max channels
ORBIS_LOG_TODO("CONTROL_INITIALIZE AT9", ORBIS_LOG_TODO("CONTROL_INITIALIZE AT9", instance.at9.inputChannels,
instance.at9.inputChannels,
instance.at9.sampleRate, instance.at9.frameSamples, instance.at9.sampleRate, instance.at9.frameSamples,
instance.at9.superFrameSize, maxChannels, instance.at9.superFrameSize, maxChannels,
outputChannels, initializeBuffer->configData, outputChannels, initializeBuffer->configData,
@ -323,8 +401,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
AVCodecContext *codecCtx = AVCodecContext *codecCtx =
avcodec_alloc_context3(instance.avCodec); avcodec_alloc_context3(instance.avCodec);
if (!codecCtx) { if (!codecCtx) {
ORBIS_LOG_FATAL( ORBIS_LOG_FATAL("Failed to allocate codec context for raw aac");
"Failed to allocate codec context for raw aac");
std::abort(); std::abort();
} }
@ -338,8 +415,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
codecCtx->ch_layout = chLayout; codecCtx->ch_layout = chLayout;
codecCtx->sample_rate = instance.aac.sampleRate; codecCtx->sample_rate = instance.aac.sampleRate;
if (int err = if (int err = avcodec_open2(codecCtx, instance.avCodec, nullptr);
avcodec_open2(codecCtx, instance.avCodec, nullptr);
err < 0) { err < 0) {
ORBIS_LOG_FATAL("Could not open codec for raw aac", err); ORBIS_LOG_FATAL("Could not open codec for raw aac", err);
std::abort(); std::abort();
@ -347,10 +423,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
instance.codecCtx = codecCtx; instance.codecCtx = codecCtx;
} }
ORBIS_LOG_TODO("CONTROL_INITIALIZE AAC", ORBIS_LOG_TODO(
(std::int16_t)instance.aac.headerType, "CONTROL_INITIALIZE AAC", (std::int16_t)instance.aac.headerType,
instance.aac.sampleRate, instance.aac.sampleRate, (std::int16_t)instance.maxChannels,
(std::int16_t)instance.maxChannels,
(orbis::uint32_t)instance.outputFormat); (orbis::uint32_t)instance.outputFormat);
} }
} }
@ -471,8 +546,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
if (instance.codec == AJM_CODEC_At9) { if (instance.codec == AJM_CODEC_At9) {
inputFrameSize = 4; inputFrameSize = 4;
outputBufferSize = av_samples_get_buffer_size( outputBufferSize = av_samples_get_buffer_size(
nullptr, instance.at9.inputChannels, nullptr, instance.at9.inputChannels, instance.at9.frameSamples,
instance.at9.frameSamples,
ajmToAvFormat(instance.outputFormat), 0); ajmToAvFormat(instance.outputFormat), 0);
} else if (instance.codec == AJM_CODEC_MP3) { } else if (instance.codec == AJM_CODEC_MP3) {
if (instance.inputBuffer.size() - totalDecodedBytes < 4) { if (instance.inputBuffer.size() - totalDecodedBytes < 4) {
@ -533,8 +607,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
instance.at9.superFrameDataIdx++; instance.at9.superFrameDataIdx++;
if (instance.at9.superFrameDataIdx == if (instance.at9.superFrameDataIdx ==
instance.at9.framesInSuperframe) { instance.at9.framesInSuperframe) {
instance.at9.estimatedSizeUsed += instance.at9.estimatedSizeUsed += instance.at9.superFrameDataLeft;
instance.at9.superFrameDataLeft;
instance.at9.superFrameDataIdx = 0; instance.at9.superFrameDataIdx = 0;
instance.at9.superFrameDataLeft = instance.at9.superFrameSize; instance.at9.superFrameDataLeft = instance.at9.superFrameSize;
} }
@ -573,8 +646,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
// we simply call this method directly (but it can be very // we simply call this method directly (but it can be very
// unstable) // unstable)
int gotFrame; int gotFrame;
int len = int len = ffcodec(instance.codecCtx->codec)
ffcodec(instance.codecCtx->codec)
->cb.decode(instance.codecCtx, frame, &gotFrame, pkt); ->cb.decode(instance.codecCtx, frame, &gotFrame, pkt);
if (len < 0) { if (len < 0) {
ORBIS_LOG_FATAL("Error during decoding AAC"); ORBIS_LOG_FATAL("Error during decoding AAC");
@ -634,8 +706,7 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
av_opt_set_sample_fmt(resampler, "out_sample_fmt", av_opt_set_sample_fmt(resampler, "out_sample_fmt",
ajmToAvFormat(instance.outputFormat), 0); ajmToAvFormat(instance.outputFormat), 0);
if (swr_init(resampler) < 0) { if (swr_init(resampler) < 0) {
ORBIS_LOG_FATAL( ORBIS_LOG_FATAL("Failed to initialize the resampling context");
"Failed to initialize the resampling context");
std::abort(); std::abort();
} }
} }
@ -657,9 +728,9 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
if (bufferOffset <= outputWritten && if (bufferOffset <= outputWritten &&
bufferOffset + buffer.size > outputWritten) { bufferOffset + buffer.size > outputWritten) {
auto byteOffset = outputWritten - bufferOffset; auto byteOffset = outputWritten - bufferOffset;
auto size = std::min(buffer.size - byteOffset, auto size =
instance.outputBuffer.size() - std::min(buffer.size - byteOffset,
bufferOutputWritten); instance.outputBuffer.size() - bufferOutputWritten);
ORBIS_RET_ON_ERROR(orbis::uwrite( ORBIS_RET_ON_ERROR(orbis::uwrite(
buffer.pOutput + byteOffset, buffer.pOutput + byteOffset,
instance.outputBuffer.data() + bufferOutputWritten, size)); instance.outputBuffer.data() + bufferOutputWritten, size));
@ -684,8 +755,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
if (runJob.flags & SIDEBAND_STREAM) { if (runJob.flags & SIDEBAND_STREAM) {
// ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, outputWritten, // ORBIS_LOG_TODO("SIDEBAND_STREAM", currentSize, outputWritten,
// instance.processedSamples); // instance.processedSamples);
auto *stream = reinterpret_cast<AJMSidebandStream *>( auto *stream = reinterpret_cast<AJMSidebandStream *>(runJob.pSideband +
runJob.pSideband + currentSize); currentSize);
stream->inputSize = totalDecodedBytes; stream->inputSize = totalDecodedBytes;
stream->outputSize = outputWritten; stream->outputSize = outputWritten;
stream->decodedSamples = instance.processedSamples; stream->decodedSamples = instance.processedSamples;
@ -697,8 +768,8 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
// (std::uint16_t)instance.lastDecode.channels, // (std::uint16_t)instance.lastDecode.channels,
// (std::uint16_t)instance.outputFormat, // (std::uint16_t)instance.outputFormat,
// instance.lastDecode.sampleRate); // instance.lastDecode.sampleRate);
auto *format = reinterpret_cast<AJMSidebandFormat *>( auto *format = reinterpret_cast<AJMSidebandFormat *>(runJob.pSideband +
runJob.pSideband + currentSize); currentSize);
format->channels = AJMChannels(instance.lastDecode.channels); format->channels = AJMChannels(instance.lastDecode.channels);
format->sampleRate = instance.lastDecode.sampleRate; format->sampleRate = instance.lastDecode.sampleRate;
format->sampleFormat = instance.outputFormat; format->sampleFormat = instance.outputFormat;
@ -753,30 +824,37 @@ static orbis::ErrorCode ajm_ioctl(orbis::File *file, std::uint64_t request,
return {}; return {};
} }
if (request == 0xc0288908) { struct AjmIoctlWaitBatchBuffer {
struct Args { orbis::uint32_t result;
orbis::uint32_t unk0; orbis::uint32_t unk0;
orbis::uint32_t unk1;
orbis::uint32_t batchId; orbis::uint32_t batchId;
orbis::uint32_t timeout; orbis::uint32_t timeout;
orbis::uint64_t batchError; orbis::uint64_t batchError;
orbis::uint32_t unk[4];
}; };
auto args = reinterpret_cast<Args *>(argp); static orbis::ErrorCode
args->unk0 = 0; ajm_ioctl_wait_batch_buffer(orbis::Thread *thread, AjmDevice *device,
// ORBIS_LOG_ERROR(__FUNCTION__, request, args->unk0, args->unk1, AjmIoctlWaitBatchBuffer &args) {
// args->batchId, args->timeout, args->batchError); args.result = 0;
// ORBIS_LOG_ERROR(__FUNCTION__, request, args.result, args.unk0,
// args.batchId, args.timeout, args.batchError);
// thread->where(); // thread->where();
return {}; return {};
} }
ORBIS_LOG_FATAL("Unhandled AJM ioctl", request); static const orbis::FileOps fileOps = {};
thread->where();
return {};
}
static const orbis::FileOps fileOps = { AjmDevice::AjmDevice() {
.ioctl = ajm_ioctl, addIoctl<AJM_IOCTL_FINALIZE>(ajm_ioctl_finalize);
}; addIoctl<AJM_IOCTL_MODULE_REGISTER>(ajm_ioctl_module_register);
addIoctl<AJM_IOCTL_MODULE_UNREGISTER>(ajm_ioctl_module_unregister);
addIoctl<AJM_IOCTL_INSTANCE_CREATE>(ajm_ioctl_instance_create);
addIoctl<AJM_IOCTL_INSTANCE_DESTROY>(ajm_ioctl_instance_destroy);
addIoctl<AJM_IOCTL_START_BATCH_BUFFER>(ajm_ioctl_start_batch_buffer);
addIoctl<AJM_IOCTL_WAIT_BATCH_BUFFER>(ajm_ioctl_wait_batch_buffer);
addIoctl<AJM_IOCTL_INSTANCE_EXTEND>(ajm_ioctl_instance_extend);
addIoctl<AJM_IOCTL_INSTANCE_SWITCH>(ajm_ioctl_instance_switch);
}
orbis::ErrorCode AjmDevice::open(rx::Ref<orbis::File> *file, const char *path, orbis::ErrorCode AjmDevice::open(rx::Ref<orbis::File> *file, const char *path,
std::uint32_t flags, std::uint32_t mode, std::uint32_t flags, std::uint32_t mode,