blob: 401f129dc5c164d9ee0d672e5f9c75a5c3f41147 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BIOD_CROS_FP_DEVICE_H_
#define BIOD_CROS_FP_DEVICE_H_
#include <sys/ioctl.h>
#include <bitset>
#include <memory>
#include <string>
#include <vector>
#include <base/files/file_util.h>
#include <base/message_loop/message_loop.h>
#include <chromeos/ec/cros_ec_dev.h>
#include <chromeos/ec/ec_commands.h>
#include "biod/uinput_device.h"
using MessageLoopForIO = base::MessageLoopForIO;
using VendorTemplate = std::vector<uint8_t>;
namespace biod {
class CrosFpDevice : public MessageLoopForIO::Watcher {
public:
using MkbpCallback = base::Callback<void(const uint32_t event)>;
explicit CrosFpDevice(const MkbpCallback& mkbp_event)
: mkbp_event_(mkbp_event),
fd_watcher_(std::make_unique<MessageLoopForIO::FileDescriptorWatcher>(
FROM_HERE)) {}
~CrosFpDevice();
struct EcVersion {
std::string ro_version;
std::string rw_version;
ec_current_image current_image = EC_IMAGE_UNKNOWN;
};
static std::unique_ptr<CrosFpDevice> Open(const MkbpCallback& callback);
// Run a simple command to get the version information from FP MCU and check
// whether the image type returned is the same as |expected_image|.
static bool WaitOnEcBoot(const base::ScopedFD& cros_fp_fd,
ec_current_image expected_image);
// Run a simple command to get the version information from FP MCU.
// The retrieved version is written to |ver|.
// Returns true if successfully retrieved the version.
static bool GetVersion(const base::ScopedFD& cros_fp_fd, EcVersion* ver);
bool FpMode(uint32_t mode);
bool GetFpMode(uint32_t* mode);
bool GetFpStats(int* capture_ms, int* matcher_ms, int* overall_ms);
bool GetDirtyMap(std::bitset<32>* bitmap);
bool GetTemplate(int index, VendorTemplate* out);
bool UploadTemplate(const VendorTemplate& tmpl);
bool SetContext(std::string user_id);
bool ResetContext();
// Initialise the entropy in the SBP. If |reset| is true, the old entropy
// will be deleted. If |reset| is false, we will only add entropy, and only
// if no entropy had been added before.
bool InitEntropy(bool reset = false);
int MaxTemplateCount() { return info_.template_max; }
int TemplateVersion() { return info_.template_version; }
// Kernel device exposing the MCU command interface.
static constexpr char kCrosFpPath[] = "/dev/cros_fp";
static constexpr int kLastTemplate = -1;
protected:
// MessageLoopForIO::Watcher overrides:
void OnFileCanReadWithoutBlocking(int fd) override;
void OnFileCanWriteWithoutBlocking(int fd) override {}
private:
bool Init();
bool EcDevInit();
ssize_t ReadVersion(char* buffer, size_t size);
bool EcProtoInfo(ssize_t* max_read, ssize_t* max_write);
bool EcReboot(ec_current_image to_image);
// Run the EC command to generate new entropy in the underlying MCU.
// |reset| specifies whether we want to merely add entropy (false), or
// perform a reset, which erases old entropy(true).
bool AddEntropy(bool reset);
// Get block id from rollback info.
bool GetRollBackInfoId(int32_t* block_id);
bool SetUpFp();
bool FpFrame(int index, std::vector<uint8_t>* frame);
bool UpdateFpInfo();
// Run a sequence of EC commands to update the entropy in the
// MCU. If |reset| is set to true, it will additionally erase the existing
// entropy too.
bool UpdateEntropy(bool reset);
base::ScopedFD cros_fd_;
ssize_t max_read_size_;
ssize_t max_write_size_;
struct ec_response_fp_info info_;
MkbpCallback mkbp_event_;
UinputDevice input_device_;
std::unique_ptr<MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
DISALLOW_COPY_AND_ASSIGN(CrosFpDevice);
};
// Empty request or response for the EcCommand template below.
struct EmptyParam {};
// empty struct is one byte in C++, get the size we want instead.
template <typename T>
constexpr size_t realsizeof() {
return std::is_empty<T>::value ? 0 : sizeof(T);
}
// Helper to build and send the command structures for cros_fp.
template <typename O, typename I>
class EcCommand {
public:
explicit EcCommand(uint32_t cmd, uint32_t ver = 0, const O& req = {})
: data_({
.cmd = {.version = ver,
.command = cmd,
.result = 0xff,
.outsize = realsizeof<O>(),
.insize = realsizeof<I>()},
.req = req,
}) {}
void SetRespSize(uint32_t insize) { data_.cmd.insize = insize; }
void SetReqSize(uint32_t outsize) { data_.cmd.outsize = outsize; }
void SetReq(const O& req) { data_.req = req; }
/**
* Run an EC command. Optionally retry the command when the underlying ioctl
* returns ETIMEDOUT.
*
* @param ec_fd file descriptor for the EC device
* @param num_attempts number of attempts to try, optional
* @param result pointer to variable to hold the return code of the command,
* optional
* @return true if command runs successfully and response size is same as
* expected, false otherwise
*
* The caller must be careful to only retry EC state-less
* commands, that can be rerun without consequence.
*/
bool Run(int ec_fd, int num_attempts = 1, uint16_t* result = nullptr) {
CHECK_GT(num_attempts, 0);
for (int retry = 0; retry < num_attempts; retry++) {
data_.cmd.result = 0xff;
// We rely on the ioctl preserving data_.req when the command fails.
// This is important for subsequent retries using the same data_.req.
int ret = ioctl(ec_fd, CROS_EC_DEV_IOCXCMD_V2, &data_);
if (ret >= 0) {
LOG_IF(INFO, retry > 0)
<< "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " succeeded on attempt " << retry + 1 << "/"
<< num_attempts << ".";
if (result != nullptr)
*result = data_.cmd.result;
return (static_cast<uint32_t>(ret) == data_.cmd.insize);
}
if (result != nullptr)
// 0xff means Run() failed and we don't have any result.
*result = 0xff;
if (errno != ETIMEDOUT) {
PLOG(ERROR) << "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " failed on attempt " << retry + 1 << "/"
<< num_attempts << ", retry is not allowed for error";
return false;
}
PLOG(ERROR) << "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " failed on attempt " << retry + 1 << "/"
<< num_attempts;
}
return false;
}
I* Resp() { return &data_.resp; }
O* Req() { return &data_.req; }
uint16_t Result() { return data_.cmd.result; }
private:
struct {
struct cros_ec_command_v2 cmd;
union {
O req;
I resp;
};
} data_;
DISALLOW_COPY_AND_ASSIGN(EcCommand);
};
} // namespace biod
#endif // BIOD_CROS_FP_DEVICE_H_