Atmosphere/stratosphere/boot/source/i2c_driver/i2c_api.cpp
2019-06-20 00:57:17 -07:00

173 lines
5.4 KiB
C++

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_api.hpp"
#include "i2c_resource_manager.hpp"
typedef Result (*CommandHandler)(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session);
static Result I2cSendHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
I2cTransactionOption option = static_cast<I2cTransactionOption>(
(((**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(I2cDriver::Send(session, *cur_cmd, num_bytes, option));
(*cur_cmd) += num_bytes;
return ResultSuccess;
}
static Result I2cReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
I2cTransactionOption option = static_cast<I2cTransactionOption>(
(((**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(I2cDriver::Receive(session, *cur_dst, num_bytes, option));
(*cur_dst) += num_bytes;
return ResultSuccess;
}
static Result I2cSubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
const I2cSubCommand sub_cmd = static_cast<I2cSubCommand>((**cur_cmd) >> 2);
(*cur_cmd)++;
switch (sub_cmd) {
case I2cSubCommand_Sleep:
{
const size_t us = (**cur_cmd);
(*cur_cmd)++;
svcSleepThread(us * 1'000ul);
}
break;
default:
std::abort();
}
return ResultSuccess;
}
static constexpr CommandHandler g_cmd_handlers[I2cCommand_Count] = {
I2cSendHandler,
I2cReceiveHandler,
I2cSubCommandHandler,
};
static inline I2cResourceManager &GetResourceManager() {
return I2cResourceManager::GetInstance();
}
static inline void CheckInitialized() {
if (!GetResourceManager().IsInitialized()) {
std::abort();
}
}
void I2cDriver::Initialize() {
GetResourceManager().Initialize();
}
void I2cDriver::Finalize() {
GetResourceManager().Finalize();
}
void I2cDriver::OpenSession(I2cSessionImpl *out_session, I2cDevice device) {
CheckInitialized();
if (!IsI2cDeviceSupported(device)) {
std::abort();
}
const I2cBus bus = GetI2cDeviceBus(device);
const u32 slave_address = GetI2cDeviceSlaveAddress(device);
const AddressingMode addressing_mode = GetI2cDeviceAddressingMode(device);
const SpeedMode speed_mode = GetI2cDeviceSpeedMode(device);
const u32 max_retries = GetI2cDeviceMaxRetries(device);
const u64 retry_wait_time = GetI2cDeviceRetryWaitTime(device);
GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time);
}
void I2cDriver::CloseSession(I2cSessionImpl &session) {
CheckInitialized();
GetResourceManager().CloseSession(session);
}
Result I2cDriver::Send(I2cSessionImpl &session, const void *src, size_t size, I2cTransactionOption option) {
CheckInitialized();
if (src == nullptr || size == 0) {
std::abort();
}
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(session.bus));
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, DriverCommand_Send);
}
Result I2cDriver::Receive(I2cSessionImpl &session, void *dst, size_t size, I2cTransactionOption option) {
CheckInitialized();
if (dst == nullptr || size == 0) {
std::abort();
}
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(session.bus));
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, DriverCommand_Receive);
}
Result I2cDriver::ExecuteCommandList(I2cSessionImpl &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) {
CheckInitialized();
if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) {
std::abort();
}
u8 *cur_dst = static_cast<u8 *>(dst);
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
const u8 *cmd_list_end = cur_cmd + cmd_list_size;
while (cur_cmd < cmd_list_end) {
I2cCommand cmd = static_cast<I2cCommand>((*cur_cmd) & 3);
if (cmd >= I2cCommand_Count) {
std::abort();
}
R_TRY(g_cmd_handlers[cmd](&cur_cmd, &cur_dst, session));
}
return ResultSuccess;
}
void I2cDriver::SuspendBuses() {
GetResourceManager().SuspendBuses();
}
void I2cDriver::ResumeBuses() {
GetResourceManager().ResumeBuses();
}
void I2cDriver::SuspendPowerBus() {
GetResourceManager().SuspendPowerBus();
}
void I2cDriver::ResumePowerBus() {
GetResourceManager().ResumePowerBus();
}