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