blob: 22c03abf076878aef1f8242097bc64716b647808 [file] [log] [blame]
Qiang XUe28d1fa2019-02-27 13:50:56 +08001/*
2// Copyright (c) 2018 Intel Corporation
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
17#include <errno.h>
18#include <fcntl.h>
19#include <sys/ioctl.h>
20#include <unistd.h>
21
22#include <ChassisIntrusionSensor.hpp>
23#include <boost/asio.hpp>
24#include <boost/bind.hpp>
25#include <chrono>
26#include <iostream>
27#include <sdbusplus/asio/object_server.hpp>
28#include <string>
29#include <thread>
30
31extern "C" {
32#include <i2c/smbus.h>
33#include <linux/i2c-dev.h>
34}
35
36static constexpr bool DEBUG = false;
37
38static constexpr unsigned int intrusionSensorPollSec = 1;
39
40// SMLink Status Register
41const static constexpr size_t pchStatusRegIntrusion = 0x04;
42
43// Status bit field masks
44const static constexpr size_t pchRegMaskIntrusion = 0x01;
45
46// Hold current PCH register values
47static unsigned int intrudeValue;
48
49// gpio sysfs path
50constexpr const char* gpioPath = "/sys/class/gpio/";
51
52void ChassisIntrusionSensor::updateValue(const std::string newValue)
53{
54 // indicate that it is internal set call
55 mInternalSet = true;
56 mIface->set_property("Status", newValue);
57
58 mValue = newValue;
59
60 if (mOldValue == "Normal" && mValue != "Normal")
61 {
62 std::cerr << "save to SEL for intrusion assert event \n";
63 // TODO: call add SEL log API, depends on patch #13956
64 mOldValue = mValue;
65 }
66 else if (mOldValue != "Normal" && mValue == "Normal")
67 {
68 std::cerr << "save to SEL for intrusion de-assert event \n";
69 // TODO: call add SEL log API, depends on patch #13956
70 mOldValue = mValue;
71 }
72}
73
74int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
75{
76 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
77
78 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
79 if (fd < 0)
80 {
81 std::cerr << "unable to open i2c device \n";
82 return -1;
83 }
84 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
85 {
86 std::cerr << "unable to set device address\n";
87 close(fd);
88 return -1;
89 }
90
91 unsigned long funcs = 0;
92 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
93 {
94 std::cerr << "not support I2C_FUNCS \n";
95 close(fd);
96 return -1;
97 }
98
99 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
100 {
101 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
102 close(fd);
103 return -1;
104 }
105
106 unsigned int statusValue;
107 unsigned int statusMask = pchRegMaskIntrusion;
108 unsigned int statusReg = pchStatusRegIntrusion;
109
110 statusValue = i2c_smbus_read_byte_data(fd, statusReg);
111 if (DEBUG)
112 {
113 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
114 << ", value = " << statusValue << "\n";
115 }
116
117 close(fd);
118
119 if (statusValue < 0)
120 {
121 std::cerr << "i2c_smbus_read_byte_data failed \n";
122 return -1;
123 }
124
125 // Get status value with mask
126 int newValue = statusValue & statusMask;
127
128 if (DEBUG)
129 {
130 std::cout << "statusValue is " << statusValue << "\n";
131 std::cout << "Intrusion sensor value is " << newValue << "\n";
132 }
133
134 return newValue;
135}
136
137void ChassisIntrusionSensor::pollSensorStatusByPch()
138{
139 // setting a new experation implicitly cancels any pending async wait
140 mPollTimer.expires_from_now(
141 boost::posix_time::seconds(intrusionSensorPollSec));
142
143 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
144 // case of timer expired
145 if (!ec)
146 {
147 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
148 std::string newValue = statusValue ? "HardwareIntrusion" : "Normal";
149
150 // save value
151 if (mOverridenState)
152 {
153 newValue = mOverriddenValue;
154 }
155
156 if (newValue != "unknown" && mValue != newValue)
157 {
158 std::cout << "update value from " << mValue << " to "
159 << newValue << "\n";
160 updateValue(newValue);
161 }
162
163 // trigger next polling
164 pollSensorStatusByPch();
165 }
166 // case of being canceled
167 else if (ec == boost::asio::error::operation_aborted)
168 {
169 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
170 return;
171 }
172 });
173}
174
175void ChassisIntrusionSensor::readGpio()
176{
177 constexpr size_t readSize = sizeof("0");
178 std::string readBuf;
179 readBuf.resize(readSize);
180 lseek(mFd, 0, SEEK_SET);
181 size_t r = ::read(mFd, readBuf.data(), readSize);
182 if (r != readSize)
183 {
184 std::cerr << "Error reading gpio\n";
185 }
186 else
187 {
188 bool value = std::stoi(readBuf);
189 if (mGpioInverted)
190 {
191 value = !value;
192 }
193
194 // set string defined in chassis redfish schema
195 std::string newValue = value ? "HardwareIntrusion" : "Normal";
196
197 if (DEBUG)
198 {
199 std::cout << "\nGPIO value is " << value << "\n";
200 std::cout << "Intrusion sensor value is " << newValue << "\n";
201 }
202
203 // save value
204 if (mOverridenState)
205 {
206 newValue = mOverriddenValue;
207 }
208
209 if (newValue != "unknown" && mValue != newValue)
210 {
211 std::cout << "update value from " << mValue << " to " << newValue
212 << "\n";
213 updateValue(newValue);
214 }
215 }
216}
217
218void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
219{
220 mInputDev.async_wait(
221 boost::asio::ip::tcp::socket::wait_error,
222 [this](const boost::system::error_code& ec) {
223 if (ec == boost::system::errc::bad_file_descriptor)
224 {
225 return; // we're being destroyed
226 }
227 else if (ec)
228 {
229 std::cerr << "Error on GPIO based intrusion sensor socket\n";
230 }
231 else
232 {
233 readGpio();
234 }
235 pollSensorStatusByGpio();
236 });
237}
238
239void ChassisIntrusionSensor::initGpioDeviceFile(const int index)
240{
241 std::string device = gpioPath + std::string("gpio") + std::to_string(index);
242 mFd = open((device + "/value").c_str(), O_RDONLY);
243 if (mFd < 0)
244 {
245 std::cerr << "Error opening gpio " << index << "\n";
246 return;
247 }
248 mInputDev.assign(boost::asio::ip::tcp::v4(), mFd);
249}
250
251int ChassisIntrusionSensor::setSensorValue(const std::string& req,
252 std::string& propertyValue)
253{
254 if (mInternalSet)
255 {
256 mInternalSet = false;
257 propertyValue = req;
258 }
259 else
260 {
261 mOverriddenValue = req;
262 mOverridenState = true;
263 }
264 return 1;
265}
266
267void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
268 int slaveAddr, int gpioIndex,
269 bool gpioInverted)
270{
271 if (DEBUG)
272 {
273 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
274 << "\n";
275 if (type == IntrusionSensorType::pch)
276 {
277 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
278 << "\n";
279 }
280 else if (type == IntrusionSensorType::gpio)
281 {
282 std::cerr << "gpioIndex = " << gpioIndex
283 << ", gpioInverted = " << gpioInverted << "\n";
284 }
285 }
286
287 if ((type == IntrusionSensorType::pch && busId == mBusId &&
288 slaveAddr == mSlaveAddr) ||
289 (type == IntrusionSensorType::gpio && gpioIndex == mGpioIndex &&
290 gpioInverted == mGpioInverted))
291 {
292 return;
293 }
294
295 mType = type;
296 mBusId = busId;
297 mSlaveAddr = slaveAddr;
298 mGpioIndex = gpioIndex;
299 mGpioInverted = gpioInverted;
300
301 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
302 (mType == IntrusionSensorType::gpio && mGpioIndex > 0))
303 {
304 // initialize first if not initialized before
305 if (!mInitialized)
306 {
307 mIface->register_property(
308 "Status", mValue,
309 [&](const std::string& req, std::string& propertyValue) {
310 return setSensorValue(req, propertyValue);
311 });
312 mIface->initialize();
313
314 if (mType == IntrusionSensorType::gpio)
315 {
316 initGpioDeviceFile(mGpioIndex);
317 }
318
319 mInitialized = true;
320 }
321
322 // start polling value
323 if (mType == IntrusionSensorType::pch)
324 {
325 pollSensorStatusByPch();
326 }
327 else if (mType == IntrusionSensorType::gpio && mFd > 0)
328 {
329 pollSensorStatusByGpio();
330 }
331 }
332
333 // invalid para, release resource
334 else
335 {
336 if (mInitialized)
337 {
338 if (mType == IntrusionSensorType::pch)
339 {
340 mPollTimer.cancel();
341 }
342 else if (mType == IntrusionSensorType::gpio)
343 {
344 mInputDev.close();
345 close(mFd);
346 }
347 mInitialized = false;
348 }
349 }
350}
351
352ChassisIntrusionSensor::ChassisIntrusionSensor(
353 boost::asio::io_service& io,
354 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
355 mPollTimer(io),
356 mIface(iface), mInputDev(io), mType(IntrusionSensorType::gpio), mBusId(-1),
357 mSlaveAddr(-1), mGpioIndex(-1), mGpioInverted(false), mValue("unknown"),
358 mOldValue("unknown")
359{
360}
361
362ChassisIntrusionSensor::~ChassisIntrusionSensor()
363{
364 if (mType == IntrusionSensorType::pch)
365 {
366 mPollTimer.cancel();
367 }
368 else if (mType == IntrusionSensorType::gpio)
369 {
370 mInputDev.close();
371 close(mFd);
372 }
373}