blob: 60d0f0df3d3669f39101e4172a4575ddd0931073 [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
Patrick Ventureca44b2f2019-10-31 11:02:26 -070017#include "ChassisIntrusionSensor.hpp"
18
Qiang XUe28d1fa2019-02-27 13:50:56 +080019#include <errno.h>
20#include <fcntl.h>
21#include <sys/ioctl.h>
22#include <unistd.h>
23
Qiang XUe28d1fa2019-02-27 13:50:56 +080024#include <boost/asio.hpp>
25#include <boost/bind.hpp>
26#include <chrono>
27#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <sdbusplus/asio/object_server.hpp>
30#include <string>
31#include <thread>
32
33extern "C" {
34#include <i2c/smbus.h>
35#include <linux/i2c-dev.h>
36}
37
38static constexpr bool DEBUG = false;
39
40static constexpr unsigned int intrusionSensorPollSec = 1;
41
42// SMLink Status Register
43const static constexpr size_t pchStatusRegIntrusion = 0x04;
44
45// Status bit field masks
46const static constexpr size_t pchRegMaskIntrusion = 0x01;
47
Qiang XUe28d1fa2019-02-27 13:50:56 +080048// gpio sysfs path
49constexpr const char* gpioPath = "/sys/class/gpio/";
50
51void ChassisIntrusionSensor::updateValue(const std::string newValue)
52{
53 // indicate that it is internal set call
54 mInternalSet = true;
55 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053056 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080057
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
James Feistb6c0b912019-07-09 12:21:44 -0700106 int statusValue;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800107 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
Qiang XUe28d1fa2019-02-27 13:50:56 +0800150 if (newValue != "unknown" && mValue != newValue)
151 {
152 std::cout << "update value from " << mValue << " to "
153 << newValue << "\n";
154 updateValue(newValue);
155 }
156
157 // trigger next polling
158 pollSensorStatusByPch();
159 }
160 // case of being canceled
161 else if (ec == boost::asio::error::operation_aborted)
162 {
163 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
164 return;
165 }
166 });
167}
168
169void ChassisIntrusionSensor::readGpio()
170{
171 constexpr size_t readSize = sizeof("0");
172 std::string readBuf;
173 readBuf.resize(readSize);
174 lseek(mFd, 0, SEEK_SET);
175 size_t r = ::read(mFd, readBuf.data(), readSize);
176 if (r != readSize)
177 {
178 std::cerr << "Error reading gpio\n";
179 }
180 else
181 {
182 bool value = std::stoi(readBuf);
183 if (mGpioInverted)
184 {
185 value = !value;
186 }
187
188 // set string defined in chassis redfish schema
189 std::string newValue = value ? "HardwareIntrusion" : "Normal";
190
191 if (DEBUG)
192 {
193 std::cout << "\nGPIO value is " << value << "\n";
194 std::cout << "Intrusion sensor value is " << newValue << "\n";
195 }
196
Qiang XUe28d1fa2019-02-27 13:50:56 +0800197 if (newValue != "unknown" && mValue != newValue)
198 {
199 std::cout << "update value from " << mValue << " to " << newValue
200 << "\n";
201 updateValue(newValue);
202 }
203 }
204}
205
206void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
207{
208 mInputDev.async_wait(
209 boost::asio::ip::tcp::socket::wait_error,
210 [this](const boost::system::error_code& ec) {
211 if (ec == boost::system::errc::bad_file_descriptor)
212 {
213 return; // we're being destroyed
214 }
215 else if (ec)
216 {
217 std::cerr << "Error on GPIO based intrusion sensor socket\n";
218 }
219 else
220 {
221 readGpio();
222 }
223 pollSensorStatusByGpio();
224 });
225}
226
227void ChassisIntrusionSensor::initGpioDeviceFile(const int index)
228{
229 std::string device = gpioPath + std::string("gpio") + std::to_string(index);
230 mFd = open((device + "/value").c_str(), O_RDONLY);
231 if (mFd < 0)
232 {
233 std::cerr << "Error opening gpio " << index << "\n";
234 return;
235 }
236 mInputDev.assign(boost::asio::ip::tcp::v4(), mFd);
237}
238
239int ChassisIntrusionSensor::setSensorValue(const std::string& req,
240 std::string& propertyValue)
241{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530242 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800243 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800244 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800245 mOverridenState = true;
246 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530247 else if (!mOverridenState)
248 {
249 propertyValue = req;
250 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800251 return 1;
252}
253
254void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
255 int slaveAddr, int gpioIndex,
256 bool gpioInverted)
257{
258 if (DEBUG)
259 {
260 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
261 << "\n";
262 if (type == IntrusionSensorType::pch)
263 {
264 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
265 << "\n";
266 }
267 else if (type == IntrusionSensorType::gpio)
268 {
269 std::cerr << "gpioIndex = " << gpioIndex
270 << ", gpioInverted = " << gpioInverted << "\n";
271 }
272 }
273
274 if ((type == IntrusionSensorType::pch && busId == mBusId &&
275 slaveAddr == mSlaveAddr) ||
276 (type == IntrusionSensorType::gpio && gpioIndex == mGpioIndex &&
277 gpioInverted == mGpioInverted))
278 {
279 return;
280 }
281
282 mType = type;
283 mBusId = busId;
284 mSlaveAddr = slaveAddr;
285 mGpioIndex = gpioIndex;
286 mGpioInverted = gpioInverted;
287
288 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
289 (mType == IntrusionSensorType::gpio && mGpioIndex > 0))
290 {
291 // initialize first if not initialized before
292 if (!mInitialized)
293 {
294 mIface->register_property(
295 "Status", mValue,
296 [&](const std::string& req, std::string& propertyValue) {
297 return setSensorValue(req, propertyValue);
298 });
299 mIface->initialize();
300
301 if (mType == IntrusionSensorType::gpio)
302 {
303 initGpioDeviceFile(mGpioIndex);
304 }
305
306 mInitialized = true;
307 }
308
309 // start polling value
310 if (mType == IntrusionSensorType::pch)
311 {
312 pollSensorStatusByPch();
313 }
314 else if (mType == IntrusionSensorType::gpio && mFd > 0)
315 {
316 pollSensorStatusByGpio();
317 }
318 }
319
320 // invalid para, release resource
321 else
322 {
323 if (mInitialized)
324 {
325 if (mType == IntrusionSensorType::pch)
326 {
327 mPollTimer.cancel();
328 }
329 else if (mType == IntrusionSensorType::gpio)
330 {
331 mInputDev.close();
332 close(mFd);
333 }
334 mInitialized = false;
335 }
336 }
337}
338
339ChassisIntrusionSensor::ChassisIntrusionSensor(
340 boost::asio::io_service& io,
341 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500342 mIface(iface),
343 mType(IntrusionSensorType::gpio), mValue("unknown"), mOldValue("unknown"),
344 mBusId(-1), mSlaveAddr(-1), mPollTimer(io), mGpioIndex(-1),
345 mGpioInverted(false), mInputDev(io)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800346{
347}
348
349ChassisIntrusionSensor::~ChassisIntrusionSensor()
350{
351 if (mType == IntrusionSensorType::pch)
352 {
353 mPollTimer.cancel();
354 }
355 else if (mType == IntrusionSensorType::gpio)
356 {
357 mInputDev.close();
358 close(mFd);
359 }
360}