/* * Copyright (c) 2018-2019 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 "i2c_api.hpp" #include "impl/i2c_resource_manager.hpp" namespace ams::i2c::driver { namespace { /* For convenience. */ using CommandHandler = Result (*)(const u8 **cur_cmd, u8 **cur_dst, Session& session); /* Command handlers. */ Result SendHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { I2cTransactionOption option = static_cast( (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) ); (*cur_cmd)++; size_t num_bytes = (**cur_cmd); (*cur_cmd)++; R_TRY(Send(session, *cur_cmd, num_bytes, option)); (*cur_cmd) += num_bytes; return ResultSuccess(); } Result ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { I2cTransactionOption option = static_cast( (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) ); (*cur_cmd)++; size_t num_bytes = (**cur_cmd); (*cur_cmd)++; R_TRY(Receive(session, *cur_dst, num_bytes, option)); (*cur_dst) += num_bytes; return ResultSuccess(); } Result SubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { const SubCommand sub_cmd = static_cast((**cur_cmd) >> 2); (*cur_cmd)++; switch (sub_cmd) { case SubCommand::Sleep: { const size_t us = (**cur_cmd); (*cur_cmd)++; svcSleepThread(us * 1'000ul); } break; AMS_UNREACHABLE_DEFAULT_CASE(); } return ResultSuccess(); } /* Command handler list. */ constexpr CommandHandler g_cmd_handlers[static_cast(Command::Count)] = { SendHandler, ReceiveHandler, SubCommandHandler, }; inline impl::ResourceManager &GetResourceManager() { return impl::ResourceManager::GetInstance(); } inline void CheckInitialized() { AMS_ASSERT(GetResourceManager().IsInitialized()); } } /* Initialization. */ void Initialize() { GetResourceManager().Initialize(); } void Finalize() { GetResourceManager().Finalize(); } /* Session management. */ void OpenSession(Session *out_session, I2cDevice device) { CheckInitialized(); AMS_ASSERT(impl::IsDeviceSupported(device)); const auto bus = impl::GetDeviceBus(device); const auto slave_address = impl::GetDeviceSlaveAddress(device); const auto addressing_mode = impl::GetDeviceAddressingMode(device); const auto speed_mode = impl::GetDeviceSpeedMode(device); const auto max_retries = impl::GetDeviceMaxRetries(device); const auto retry_wait_time = impl::GetDeviceRetryWaitTime(device); GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time); } void CloseSession(Session &session) { CheckInitialized(); GetResourceManager().CloseSession(session); } /* Communication. */ Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) { CheckInitialized(); AMS_ASSERT(src != nullptr); AMS_ASSERT(size > 0); std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send); } Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) { CheckInitialized(); AMS_ASSERT(dst != nullptr); AMS_ASSERT(size > 0); std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive); } Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) { CheckInitialized(); AMS_ASSERT(dst != nullptr && size > 0); AMS_ASSERT(cmd_list != nullptr && cmd_list_size > 0); u8 *cur_dst = static_cast(dst); const u8 *cur_cmd = static_cast(cmd_list); const u8 *cmd_list_end = cur_cmd + cmd_list_size; while (cur_cmd < cmd_list_end) { Command cmd = static_cast((*cur_cmd) & 3); AMS_ASSERT(cmd < Command::Count); R_TRY(g_cmd_handlers[static_cast(cmd)](&cur_cmd, &cur_dst, session)); } return ResultSuccess(); } /* Power management. */ void SuspendBuses() { GetResourceManager().SuspendBuses(); } void ResumeBuses() { GetResourceManager().ResumeBuses(); } void SuspendPowerBus() { GetResourceManager().SuspendPowerBus(); } void ResumePowerBus() { GetResourceManager().ResumePowerBus(); } }