blob: 10cf6175f6d29260397df5b306dd3df43489f616 [file] [log] [blame]
Patrick Venturef7186a62018-11-21 14:08:04 -08001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Patrick Venture38e8c6e2018-10-24 09:23:35 -070017#include "i2c.hpp"
18
19#include <fcntl.h>
20#include <linux/i2c-dev.h>
21#include <linux/i2c.h>
22#include <sys/ioctl.h>
23#include <unistd.h>
24
25#include <array>
26#include <cerrno>
27#include <cstring>
28#include <host-ipmid/iana.hpp>
29#include <host-ipmid/oemopenbmc.hpp>
30#include <memory>
31
32namespace oem
33{
34namespace i2c
35{
36
37// Instance object.
38std::unique_ptr<I2c> globalOemI2c;
39
40// Block read (I2C_M_RECV_LEN) reads count byte, then that many bytes,
41// then possibly a checksum byte. The specified byte count limit,
42// I2C_SMBUS_BLOCK_MAX, is 32, but it seems intractible to prove
43// every I2C implementation uses that limit. So to prevent overflow,
44// allocate buffers based on the largest possible count byte of 255.
45constexpr size_t maxRecvLenBuf = 1 + 255 + 1;
46typedef std::array<uint8_t, maxRecvLenBuf> BlockBuf;
47
48struct ParsedStep
49{
50 const uint8_t* reqData;
51 size_t length;
52 DevAddr devAddr;
53 bool isRead;
54 // When nonzero, device supplies count for this step, as in SMBUS block
55 // mode. Value will tell driver how many extra bytes to read for entire
56 // block: 1 for count byte w/o PEC, 2 if there is also a PEC byte.
57 uint16_t blockExtra;
58 bool noStart;
59};
60
61struct ParsedReq
62{
63 BusId localbus;
64 bool usePec;
65 size_t numSteps;
66 std::array<ParsedStep, maxSteps> step;
67};
68
69static ipmi_ret_t parseReqHdr(const uint8_t* reqBuf, size_t reqLen,
70 size_t* bytesUsed, i2c::ParsedReq* req)
71{
72 // Request header selects bus & flags for operation;
73 // additional bytes beyond are to be interpreted as steps.
74 if (reqLen < *bytesUsed + requestHeaderLen)
75 {
76 std::fprintf(stderr, "i2c::parse reqLen=%zu?\n", reqLen);
77 return IPMI_CC_REQ_DATA_LEN_INVALID;
78 }
79 // Deserialize request header bytes.
80 req->localbus = reqBuf[requestHeaderBus];
81 auto reqFlags = reqBuf[requestHeaderFlags];
82 *bytesUsed += requestHeaderLen;
83
84 // Decode flags.
85 req->usePec = !!(reqFlags & requestFlagsUsePec);
86 return IPMI_CC_OK;
87}
88
89static ipmi_ret_t parseReqStep(const uint8_t* reqBuf, size_t reqLen,
90 size_t* bytesUsed, i2c::ParsedReq* req)
91{
92 size_t bytesLeft = reqLen - *bytesUsed;
93 if (req->numSteps >= maxSteps || bytesLeft < stepHeaderLen)
94 {
95 std::fprintf(stderr, "i2c::parse[%zu] bytesLeft=%zu?\n", req->numSteps,
96 bytesLeft);
97 return IPMI_CC_REQ_DATA_LEN_INVALID;
98 }
99 const uint8_t* stepHdr = reqBuf + *bytesUsed;
100 auto step = &req->step[req->numSteps++];
101
102 // Deserialize request step header bytes.
103 uint8_t devAndDir = stepHdr[stepHeaderDevAndDir];
104 uint8_t stepFlags = stepHdr[stepHeaderFlags];
105 step->length = stepHdr[stepHeaderParm];
106 bytesLeft -= stepHeaderLen;
107 *bytesUsed += stepHeaderLen;
108
109 // Decode device addr & direction.
110 step->devAddr = devAndDir >> 1;
111 step->isRead = !!(devAndDir & 1);
112
113 // Decode step flags.
114 step->noStart = !!(stepFlags & stepFlagsNoStart);
115
116 if (step->isRead)
117 {
118 // Read could select blockExtra.
119 if (stepFlags & stepFlagsRecvLen)
120 {
121 step->blockExtra = req->usePec ? 2 : 1;
122 }
123 }
124 else
125 {
126 // For write, requested byte count must follow.
127 if (bytesLeft < step->length)
128 {
129 std::fprintf(stderr, "i2c::parse[%zu] bytesLeft=%zu, parm=%zu?\n",
130 req->numSteps, bytesLeft, step->length);
131 return IPMI_CC_REQ_DATA_LEN_INVALID;
132 }
133 step->reqData = reqBuf + *bytesUsed;
134 *bytesUsed += step->length;
135 }
136 return IPMI_CC_OK;
137}
138
139// Parse i2c request.
140static ipmi_ret_t parse(const uint8_t* reqBuf, size_t reqLen,
141 i2c::ParsedReq* req)
142{
143 size_t bytesUsed = 0;
144 auto rc = parseReqHdr(reqBuf, reqLen, &bytesUsed, req);
145 if (rc != IPMI_CC_OK)
146 {
147 return rc;
148 }
149 do
150 {
151 rc = parseReqStep(reqBuf, reqLen, &bytesUsed, req);
152 if (rc != IPMI_CC_OK)
153 {
154 return rc;
155 }
156 } while (bytesUsed < reqLen);
157 return IPMI_CC_OK;
158}
159
160// Convert parsed request to I2C messages.
161static ipmi_ret_t buildI2cMsgs(const i2c::ParsedReq& req,
162 std::unique_ptr<i2c::BlockBuf> rxBuf[],
163 struct i2c_msg msgs[],
164 struct i2c_rdwr_ioctl_data* msgset)
165{
166 size_t minReplyLen = 0;
167
168 for (size_t i = 0; i < req.numSteps; ++msgset->nmsgs, ++i)
169 {
170 const auto& step = req.step[i];
171 auto* msg = &msgs[i];
172 msg->addr = step.devAddr;
173
174 if (!step.isRead)
175 {
176 msg->flags = 0;
177 msg->len = step.length;
178 msg->buf = const_cast<uint8_t*>(step.reqData);
179 continue;
180 }
181 rxBuf[i] = std::make_unique<i2c::BlockBuf>();
182 msg->buf = rxBuf[i]->data();
183
184 if (step.blockExtra == 0)
185 {
186 msg->flags = I2C_M_RD;
187 msg->len = step.length;
188 minReplyLen += msg->len;
189 }
190 else
191 {
192 // Special buffer setup needed for block read:
193 // . 1. msg len must allow for maximum possible transfer,
194 // . 2. blockExtra must be preloaded into buf[0]
195 // The internal i2c_transfer API is slightly different;
196 // the rdwr ioctl handler adapts by moving blockExtra
197 // into msg.len where the driver will expect to find it.
198 //
199 // References:
200 // drivers/i2c/i2c-dev.c: i2cdev_ioctl_rdwr()
201 msg->flags = I2C_M_RD | I2C_M_RECV_LEN;
202 msg->len = maxRecvLenBuf;
203 msg->buf[0] = step.blockExtra;
204 minReplyLen += step.blockExtra;
205 }
206 }
207
208 if (minReplyLen > i2c::largestReply)
209 {
210 std::fprintf(stderr, "I2c::transfer minReplyLen=%zu?\n", minReplyLen);
211 return IPMI_CC_RESPONSE_ERROR; // Won't fit in response message
212 }
213
214#ifdef __IPMI_DEBUG__
215 for (size_t i = 0; i < req.numSteps; ++i)
216 {
217 auto* msg = &msgs[i];
218 std::fprintf(stderr, "I2c::transfer msg[%zu]: %02x %04x %d\n", i,
219 msg->addr, msg->flags, msg->len);
220 }
221#endif
222
223 return IPMI_CC_OK;
224}
225
226static int openBus(BusId localbus)
227{
228 char busCharDev[16];
229 std::snprintf(busCharDev, sizeof(busCharDev) - 1, "/dev/i2c-%d", localbus);
230 int busFd = open(busCharDev, O_RDWR);
231 if (busFd < 0)
232 {
233 std::fprintf(stderr,
234 "NetFn:[0x2E], OEM:[0x002B79], Cmd:[0x02], "
235 "I2C Bus Open(\"%s\"): \"%s\"\n",
236 busCharDev, strerror(-busFd));
237 }
238 return busFd;
239}
240
241} // namespace i2c
242
243ipmi_ret_t I2c::transfer(ipmi_cmd_t cmd, const uint8_t* reqBuf,
244 uint8_t* replyBuf, size_t* dataLen)
245{
246 // Parse message header.
247 auto reqLen = *dataLen;
248 *dataLen = 0;
249 i2c::ParsedReq req = {};
250 auto rc = parse(reqBuf, reqLen, &req);
251 if (rc != IPMI_CC_OK)
252 {
253 return rc;
254 }
255
256 // Build full msgset
257 std::unique_ptr<i2c::BlockBuf> rxBuf[i2c::maxSteps];
258 struct i2c_msg msgs[i2c::maxSteps] = {};
259 struct i2c_rdwr_ioctl_data msgset = {
260 .msgs = msgs,
261 .nmsgs = 0,
262 };
263 rc = buildI2cMsgs(req, rxBuf, msgs, &msgset);
264 if (rc != IPMI_CC_OK)
265 {
266 return rc;
267 }
268
269 // Try to open i2c bus
270 int busFd = i2c::openBus(req.localbus);
271 if (busFd < 0)
272 {
273 return IPMI_CC_UNSPECIFIED_ERROR;
274 }
275 int ioError = ioctl(busFd, I2C_RDWR, &msgset);
276
277 // Done with busFd, so close it immediately to avoid leaking it.
278 (void)close(busFd);
279 if (ioError < 0)
280 {
281 std::fprintf(stderr, "I2c::transfer I2C_RDWR ioError=%d?\n", ioError);
282 return IPMI_CC_UNSPECIFIED_ERROR; // I2C_RDWR I/O error
283 }
284
285 // If we read any data, append it, in the order we read it.
286 uint8_t* nextReplyByte = replyBuf;
287 size_t replyLen = 0;
288 for (size_t i = 0; i < req.numSteps; ++i)
289 {
290 const auto& step = req.step[i];
291 if (step.isRead)
292 {
293 const auto* msg = &msgs[i];
294 size_t lenRead =
295 step.blockExtra ? *msg->buf + step.blockExtra : msg->len;
296 replyLen += lenRead;
297 if (replyLen > i2c::largestReply)
298 {
299 std::fprintf(stderr, "I2c::transfer[%zu] replyLen=%zu?\n", i,
300 replyLen);
301 return IPMI_CC_RESPONSE_ERROR; // Won't fit in response message
302 }
303 std::memcpy(nextReplyByte, msg->buf, lenRead);
304 nextReplyByte += lenRead;
305 }
306 }
307 *dataLen = replyLen;
308 return IPMI_CC_OK;
309}
310
311void I2c::registerWith(Router* oemRouter)
312{
313 Handler f = [this](ipmi_cmd_t cmd, const uint8_t* reqBuf, uint8_t* replyBuf,
314 size_t* dataLen) {
315 return transfer(cmd, reqBuf, replyBuf, dataLen);
316 };
317
318 std::fprintf(stderr, "Registering OEM:[%#08X], Cmd:[%#04X] for I2C\n",
319 googOemNumber, Cmd::i2cCmd);
320 oemRouter->registerHandler(googOemNumber, Cmd::i2cCmd, f);
321
322 std::fprintf(stderr, "Registering OEM:[%#08X], Cmd:[%#04X] for I2C\n",
323 obmcOemNumber, Cmd::i2cCmd);
324 oemRouter->registerHandler(obmcOemNumber, Cmd::i2cCmd, f);
325}
326
327namespace i2c
328{
329// Currently ipmid dynamically loads providers such as these;
330// this creates our singleton upon load.
331void setupGlobalOemI2c() __attribute__((constructor));
332
333void setupGlobalOemI2c()
334{
335 globalOemI2c = std::make_unique<I2c>();
336 globalOemI2c->registerWith(oem::mutableRouter());
337}
338} // namespace i2c
339} // namespace oem