-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathec_command.h
161 lines (133 loc) · 4.87 KB
/
ec_command.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2019 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_EC_COMMAND_H_
#define BIOD_EC_COMMAND_H_
#include <sys/ioctl.h>
#include <cerrno>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <base/logging.h>
#include <base/macros.h>
#include <chromeos/ec/cros_ec_dev.h>
namespace biod {
enum class EcCmdVersionSupportStatus {
UNKNOWN = 0,
SUPPORTED = 1,
UNSUPPORTED = 2,
};
// 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);
}
constexpr uint32_t kVersionZero = 0;
constexpr uint32_t kVersionOne = 1;
static constexpr auto kEcCommandUninitializedResult =
std::numeric_limits<uint32_t>::max();
class EcCommandInterface {
public:
virtual ~EcCommandInterface() = default;
virtual bool Run(int fd) = 0;
virtual bool RunWithMultipleAttempts(int fd, int num_attempts) = 0;
virtual uint32_t Version() const = 0;
virtual uint32_t Command() const = 0;
};
// Helper to build and send the command structures for cros_fp.
template <typename O, typename I>
class EcCommand : public EcCommandInterface {
public:
explicit EcCommand(uint32_t cmd, uint32_t ver = 0, const O& req = {})
: data_({
.cmd = {.version = ver,
.command = cmd,
.outsize = realsizeof<O>(),
.insize = realsizeof<I>(),
.result = kEcCommandUninitializedResult},
.req = req,
}) {}
EcCommand(const EcCommand&) = delete;
EcCommand& operator=(const EcCommand&) = delete;
~EcCommand() override = default;
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.
*
* @param ec_fd file descriptor for the EC device
* @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) override {
data_.cmd.result = kEcCommandUninitializedResult;
// 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) {
// If the ioctl fails for some reason let's make sure that the driver
// didn't touch the result.
data_.cmd.result = kEcCommandUninitializedResult;
PLOG(ERROR) << "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " failed";
return false;
}
return (static_cast<uint32_t>(ret) == data_.cmd.insize);
}
bool RunWithMultipleAttempts(int fd, int num_attempts) override {
for (int retry = 0; retry < num_attempts; retry++) {
bool ret = Run(fd);
if (ret) {
LOG_IF(INFO, retry > 0)
<< "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " succeeded on attempt " << retry + 1 << "/"
<< num_attempts << ".";
return true;
}
// If we just want to check the supported version of a command, and the
// command does not exist, do not emit error in the log and do not retry.
if (Command() == EC_CMD_GET_CMD_VERSIONS &&
Result() == EC_RES_INVALID_PARAM)
return false;
if (errno != ETIMEDOUT) {
LOG(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;
}
LOG(ERROR) << "FPMCU ioctl command 0x" << std::hex << data_.cmd.command
<< std::dec << " failed on attempt " << retry + 1 << "/"
<< num_attempts;
}
return false;
}
virtual I* Resp() { return &data_.resp; }
virtual const I* Resp() const { return &data_.resp; }
uint32_t RespSize() const { return data_.cmd.insize; }
O* Req() { return &data_.req; }
const O* Req() const { return &data_.req; }
virtual uint32_t Result() const { return data_.cmd.result; }
uint32_t Version() const override { return data_.cmd.version; }
uint32_t Command() const override { return data_.cmd.command; }
struct Data {
struct cros_ec_command_v2 cmd;
union {
O req;
I resp;
};
};
protected:
Data data_;
private:
virtual int ioctl(int fd, uint32_t request, Data* data) {
return ::ioctl(fd, request, data);
}
};
} // namespace biod
#endif // BIOD_EC_COMMAND_H_