diff --git a/libstratosphere/include/stratosphere/i2c.hpp b/libstratosphere/include/stratosphere/i2c.hpp index dad6bc5b..cff61261 100644 --- a/libstratosphere/include/stratosphere/i2c.hpp +++ b/libstratosphere/include/stratosphere/i2c.hpp @@ -17,6 +17,7 @@ #pragma once #include #include +#include #include #include #include diff --git a/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp b/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp new file mode 100644 index 00000000..8a0e83c2 --- /dev/null +++ b/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include + +namespace ams::i2c { + + constexpr inline size_t CommandListLengthMax = 0x100; + constexpr inline size_t CommandListReceiveCommandSize = 2; + constexpr inline size_t CommandListSendCommandSize = 2; + constexpr inline size_t CommandListSleepCommandSize = 2; + + class CommandListFormatter { + NON_COPYABLE(CommandListFormatter); + NON_MOVEABLE(CommandListFormatter); + private: + size_t current_index; + size_t command_list_length; + void *command_list; + private: + Result IsEnqueueAble(size_t sz) const; + public: + CommandListFormatter(void *p, size_t sz) : current_index(0), command_list_length(sz), command_list(p) { + AMS_ABORT_UNLESS(this->command_list_length <= CommandListLengthMax); + } + + ~CommandListFormatter() { this->command_list = nullptr; } + + size_t GetCurrentLength() const { return this->current_index; } + const void *GetListHead() const { return this->command_list; } + + Result EnqueueReceiveCommand(i2c::TransactionOption option, size_t size); + Result EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size); + Result EnqueueSleepCommand(int us); + }; + +} diff --git a/libstratosphere/source/i2c/i2c_command_list_formatter.cpp b/libstratosphere/source/i2c/i2c_command_list_formatter.cpp new file mode 100644 index 00000000..eaf6cbd3 --- /dev/null +++ b/libstratosphere/source/i2c/i2c_command_list_formatter.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "impl/i2c_command_list_format.hpp" + +namespace ams::i2c { + + Result CommandListFormatter::IsEnqueueAble(size_t sz) const { + R_UNLESS(this->command_list_length - this->current_index >= sz, ResultCommandListFull()); + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueReceiveCommand(i2c::TransactionOption option, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Receive); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength + size)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Send); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + /* Copy the data we're sending. */ + std::memcpy(cmd_list + this->current_index, src, size); + this->current_index += size; + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSleepCommand(int us) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Extension); + header0.Set(impl::SubCommandId_Sleep); + + header1.Set(us); + + return ResultSuccess(); + } + +} diff --git a/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp b/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp new file mode 100644 index 00000000..c4f4ad92 --- /dev/null +++ b/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::i2c::impl { + + enum CommandId { + CommandId_Send = 0, + CommandId_Receive = 1, + CommandId_Extension = 2, + CommandId_Count = 3, + }; + + enum SubCommandId { + SubCommandId_Sleep = 0, + }; + + struct CommonCommandFormat { + using CommandId = util::BitPack8::Field<0, 2>; + using SubCommandId = util::BitPack8::Field<2, 6>; + }; + + struct ReceiveCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SendCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SleepCommandFormat { + using MicroSeconds = util::BitPack8::Field<0, 8>; + }; + +} diff --git a/libvapours/include/vapours/results/i2c_results.hpp b/libvapours/include/vapours/results/i2c_results.hpp index 6204723e..c1aa308a 100644 --- a/libvapours/include/vapours/results/i2c_results.hpp +++ b/libvapours/include/vapours/results/i2c_results.hpp @@ -23,7 +23,7 @@ namespace ams::i2c { R_DEFINE_ERROR_RESULT(NoAck, 1); R_DEFINE_ERROR_RESULT(BusBusy, 2); - R_DEFINE_ERROR_RESULT(FullCommandList, 3); + R_DEFINE_ERROR_RESULT(CommandListFull, 3); R_DEFINE_ERROR_RESULT(UnknownDevice, 5);