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