// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include "common/concepts.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" namespace Service::Nvidia::Devices { struct IoctlOneArgTraits { template static A GetFirstArgImpl(R (T::*)(A, B...)); }; struct IoctlTwoArgTraits { template static A GetFirstArgImpl(R (T::*)(A, B, C...)); template static B GetSecondArgImpl(R (T::*)(A, B, C...)); }; struct Null {}; // clang-format off template NvResult WrapGeneric(F&& callable, std::span input, std::span inline_input, std::span output, std::span inline_output) { constexpr bool HasFixedArg = !std::is_same_v; constexpr bool HasVarArg = !std::is_same_v; constexpr bool HasInlInVarArg = !std::is_same_v; constexpr bool HasInlOutVarArg = !std::is_same_v; // Declare the fixed-size input value. FixedArg fixed{}; size_t var_offset = 0; if constexpr (HasFixedArg) { // Read the fixed-size input value. var_offset = std::min(sizeof(FixedArg), input.size()); if (var_offset > 0) { std::memcpy(&fixed, input.data(), var_offset); } } // Read the variable-sized inputs. const size_t num_var_args = HasVarArg ? ((input.size() - var_offset) / sizeof(VarArg)) : 0; std::vector var_args(num_var_args); if constexpr (HasVarArg) { if (num_var_args > 0) { std::memcpy(var_args.data(), input.data() + var_offset, num_var_args * sizeof(VarArg)); } } const size_t num_inl_in_var_args = HasInlInVarArg ? (inline_input.size() / sizeof(InlInVarArg)) : 0; std::vector inl_in_var_args(num_inl_in_var_args); if constexpr (HasInlInVarArg) { if (num_inl_in_var_args > 0) { std::memcpy(inl_in_var_args.data(), inline_input.data(), num_inl_in_var_args * sizeof(InlInVarArg)); } } // Construct inline output data. const size_t num_inl_out_var_args = HasInlOutVarArg ? (inline_output.size() / sizeof(InlOutVarArg)) : 0; std::vector inl_out_var_args(num_inl_out_var_args); // Perform the call. NvResult result = callable(fixed, var_args, inl_in_var_args, inl_out_var_args); // Copy outputs. if constexpr (HasFixedArg) { if (output.size() > 0) { std::memcpy(output.data(), &fixed, std::min(output.size(), sizeof(FixedArg))); } } if constexpr (HasVarArg) { if (num_var_args > 0 && output.size() > var_offset) { const size_t max_var_size = output.size() - var_offset; std::memcpy(output.data() + var_offset, var_args.data(), std::min(max_var_size, num_var_args * sizeof(VarArg))); } } // Copy inline outputs. if constexpr (HasInlOutVarArg) { if (num_inl_out_var_args > 0) { std::memcpy(inline_output.data(), inl_out_var_args.data(), num_inl_out_var_args * sizeof(InlOutVarArg)); } } // We're done. return result; } template NvResult WrapFixed(Self* self, F&& callable, std::span input, std::span output, Rest&&... rest) { using FixedArg = typename std::remove_reference_t; const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { return (self->*callable)(fixed, std::forward(rest)...); }; return WrapGeneric(std::move(Callable), input, {}, output, {}); } template NvResult WrapFixedInlOut(Self* self, F&& callable, std::span input, std::span output, std::span inline_output, Rest&&... rest) { using FixedArg = typename std::remove_reference_t; using InlOutVarArg = typename std::remove_reference_t::value_type; const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { return (self->*callable)(fixed, inl_out, std::forward(rest)...); }; return WrapGeneric(std::move(Callable), input, {}, output, inline_output); } template NvResult WrapVariable(Self* self, F&& callable, std::span input, std::span output, Rest&&... rest) { using VarArg = typename std::remove_reference_t::value_type; const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { return (self->*callable)(var, std::forward(rest)...); }; return WrapGeneric(std::move(Callable), input, {}, output, {}); } template NvResult WrapFixedVariable(Self* self, F&& callable, std::span input, std::span output, Rest&&... rest) { using FixedArg = typename std::remove_reference_t; using VarArg = typename std::remove_reference_t::value_type; const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { return (self->*callable)(fixed, var, std::forward(rest)...); }; return WrapGeneric(std::move(Callable), input, {}, output, {}); } template NvResult WrapFixedInlIn(Self* self, F&& callable, std::span input, std::span inline_input, std::span output, Rest&&... rest) { using FixedArg = typename std::remove_reference_t; using InlInVarArg = typename std::remove_reference_t::value_type; const auto Callable = [&](auto& fixed, auto& var, auto& inl_in, auto& inl_out) -> NvResult { return (self->*callable)(fixed, inl_in, std::forward(rest)...); }; return WrapGeneric(std::move(Callable), input, inline_input, output, {}); } // clang-format on } // namespace Service::Nvidia::Devices