blob: f406631c7c72d9100ad22d4041aed77315a943f7 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIBEC_I2C_PASSTHRU_COMMAND_H_
#define LIBEC_I2C_PASSTHRU_COMMAND_H_
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
#include <base/containers/span.h>
#include <brillo/brillo_export.h>
#include "libec/ec_command.h"
#include "libec/i2c_passthru_params.h"
namespace ec {
class BRILLO_EXPORT I2cPassthruCommand
: public EcCommand<i2c_passthru::Params, i2c_passthru::Response> {
public:
// Use factory method instead.
I2cPassthruCommand() : EcCommand(EC_CMD_I2C_PASSTHRU) {}
~I2cPassthruCommand() override = default;
// Factory method.
// @param port I2C port number
// @param addr I2C target address in 7-bit
// @param write_data data to write
// @param read_len number of bytes to read
// @return a pointer to the command or |nullptr| if error
template <typename T = I2cPassthruCommand>
static std::unique_ptr<T> Create(uint8_t port,
uint8_t addr,
const std::vector<uint8_t>& write_data,
size_t read_len) {
static_assert(
std::is_base_of_v<I2cPassthruCommand, T>,
"Only classes derived from I2cPassthruCommand can use Create");
auto cmd = std::make_unique<T>();
i2c_passthru::Params request{
.req = {.port = port,
.num_msgs = static_cast<uint8_t>((write_data.size() > 0) +
(read_len > 0))}};
CHECK_LE(request.req.num_msgs, 2);
using PassthruMessage = struct ec_params_i2c_passthru_msg;
constexpr size_t message_size = realsizeof<PassthruMessage>;
size_t req_size = realsizeof<decltype(request.req)> +
message_size * request.req.num_msgs + write_data.size();
size_t resp_size = realsizeof<decltype(cmd->Resp()->resp)> + read_len;
if (req_size > kMaxPacketSize) {
LOG(ERROR) << "write_data size (" << static_cast<int>(write_data.size())
<< ") too large";
return nullptr;
}
if (read_len > i2c_passthru::kResponseDataMaxSize) {
LOG(ERROR) << "read_len (" << static_cast<int>(read_len)
<< ") should not be greater than "
<< i2c_passthru::kResponseDataMaxSize;
return nullptr;
}
base::span<PassthruMessage> messages(
reinterpret_cast<PassthruMessage*>(request.msg_and_payload.data()),
request.req.num_msgs);
auto message_it = messages.begin();
if (write_data.size() > 0) {
message_it->addr_flags = addr;
message_it->len = write_data.size();
auto payload = request.msg_and_payload.begin() + messages.size_bytes();
std::copy(write_data.begin(), write_data.end(), payload);
++message_it;
}
if (read_len > 0) {
message_it->addr_flags = addr | EC_I2C_FLAG_READ;
message_it->len = read_len;
}
cmd->SetReq(request);
cmd->SetReqSize(req_size);
cmd->SetRespSize(resp_size);
return cmd;
}
// Returns the status code from the response of the I2C command.
virtual uint8_t I2cStatus() const { return Resp()->resp.i2c_status; }
// Returns a byte array containing the data from the response of the
// I2C command.
virtual base::span<const uint8_t> RespData() const;
};
static_assert(!std::is_copy_constructible_v<I2cPassthruCommand>,
"EcCommands are not copyable by default");
static_assert(!std::is_copy_assignable_v<I2cPassthruCommand>,
"EcCommands are not copy-assignable by default");
} // namespace ec
#endif // LIBEC_I2C_PASSTHRU_COMMAND_H_