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