blob: 8293bab60bb689265211619fa459bded6ab5475f [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>
James Feist38fb5982020-05-28 10:09:54 -070026#include <sdbusplus/asio/object_server.hpp>
27
Qiang XUe28d1fa2019-02-27 13:50:56 +080028#include <chrono>
29#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080031#include <string>
32#include <thread>
33
James Feist38fb5982020-05-28 10:09:54 -070034extern "C"
35{
Qiang XUe28d1fa2019-02-27 13:50:56 +080036#include <i2c/smbus.h>
37#include <linux/i2c-dev.h>
38}
39
40static constexpr bool DEBUG = false;
41
42static constexpr unsigned int intrusionSensorPollSec = 1;
43
44// SMLink Status Register
45const static constexpr size_t pchStatusRegIntrusion = 0x04;
46
47// Status bit field masks
48const static constexpr size_t pchRegMaskIntrusion = 0x01;
49
Qiang XUe28d1fa2019-02-27 13:50:56 +080050void ChassisIntrusionSensor::updateValue(const std::string newValue)
51{
Josh Lehan833661a2020-03-04 17:35:41 -080052 // Take no action if value already equal
53 // Same semantics as Sensor::updateValue(const double&)
54 if (newValue == mValue)
55 {
56 return;
57 }
58
Qiang XUe28d1fa2019-02-27 13:50:56 +080059 // indicate that it is internal set call
60 mInternalSet = true;
61 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053062 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080063
64 mValue = newValue;
65
66 if (mOldValue == "Normal" && mValue != "Normal")
67 {
68 std::cerr << "save to SEL for intrusion assert event \n";
69 // TODO: call add SEL log API, depends on patch #13956
70 mOldValue = mValue;
71 }
72 else if (mOldValue != "Normal" && mValue == "Normal")
73 {
74 std::cerr << "save to SEL for intrusion de-assert event \n";
75 // TODO: call add SEL log API, depends on patch #13956
76 mOldValue = mValue;
77 }
78}
79
80int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
81{
82 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
83
84 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
85 if (fd < 0)
86 {
87 std::cerr << "unable to open i2c device \n";
88 return -1;
89 }
90 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
91 {
92 std::cerr << "unable to set device address\n";
93 close(fd);
94 return -1;
95 }
96
97 unsigned long funcs = 0;
98 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
99 {
100 std::cerr << "not support I2C_FUNCS \n";
101 close(fd);
102 return -1;
103 }
104
105 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
106 {
107 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
108 close(fd);
109 return -1;
110 }
111
James Feistb6c0b912019-07-09 12:21:44 -0700112 int statusValue;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800113 unsigned int statusMask = pchRegMaskIntrusion;
114 unsigned int statusReg = pchStatusRegIntrusion;
115
116 statusValue = i2c_smbus_read_byte_data(fd, statusReg);
117 if (DEBUG)
118 {
119 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
120 << ", value = " << statusValue << "\n";
121 }
122
123 close(fd);
124
125 if (statusValue < 0)
126 {
127 std::cerr << "i2c_smbus_read_byte_data failed \n";
128 return -1;
129 }
130
131 // Get status value with mask
132 int newValue = statusValue & statusMask;
133
134 if (DEBUG)
135 {
136 std::cout << "statusValue is " << statusValue << "\n";
137 std::cout << "Intrusion sensor value is " << newValue << "\n";
138 }
139
140 return newValue;
141}
142
143void ChassisIntrusionSensor::pollSensorStatusByPch()
144{
145 // setting a new experation implicitly cancels any pending async wait
146 mPollTimer.expires_from_now(
147 boost::posix_time::seconds(intrusionSensorPollSec));
148
149 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
150 // case of timer expired
151 if (!ec)
152 {
153 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
154 std::string newValue = statusValue ? "HardwareIntrusion" : "Normal";
155
Qiang XUe28d1fa2019-02-27 13:50:56 +0800156 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{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800177 mGpioLine.event_read();
178 auto value = mGpioLine.get_value();
179
180 // set string defined in chassis redfish schema
181 std::string newValue = value ? "HardwareIntrusion" : "Normal";
182
183 if (DEBUG)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800184 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800185 std::cout << "\nGPIO value is " << value << "\n";
186 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800187 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800188
189 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800190 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800191 std::cout << "update value from " << mValue << " to " << newValue
192 << "\n";
193 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800194 }
195}
196
197void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
198{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800199 mGpioFd.async_wait(
200 boost::asio::posix::stream_descriptor::wait_read,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800201 [this](const boost::system::error_code& ec) {
202 if (ec == boost::system::errc::bad_file_descriptor)
203 {
204 return; // we're being destroyed
205 }
206 else if (ec)
207 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800208 std::cerr
209 << "Error on GPIO based intrusion sensor wait event\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800210 }
211 else
212 {
213 readGpio();
214 }
215 pollSensorStatusByGpio();
216 });
217}
218
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800219void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800220{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800221 mGpioLine = gpiod::find_line(mPinName);
222 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800223 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800224 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
225 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800226 return;
227 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800228
229 try
230 {
231
232 mGpioLine.request(
233 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
234 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
235
236 // set string defined in chassis redfish schema
237 auto value = mGpioLine.get_value();
238 std::string newValue = value ? "HardwareIntrusion" : "Normal";
239 updateValue(newValue);
240
241 auto gpioLineFd = mGpioLine.event_get_fd();
242 if (gpioLineFd < 0)
243 {
244 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
245 << " fd\n";
246 return;
247 }
248
249 mGpioFd.assign(gpioLineFd);
250 }
251 catch (std::system_error&)
252 {
253 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
254 << mPinName << "\n";
255 return;
256 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800257}
258
259int ChassisIntrusionSensor::setSensorValue(const std::string& req,
260 std::string& propertyValue)
261{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530262 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800263 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800264 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800265 mOverridenState = true;
266 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530267 else if (!mOverridenState)
268 {
269 propertyValue = req;
270 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800271 return 1;
272}
273
274void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800275 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800276{
277 if (DEBUG)
278 {
279 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
280 << "\n";
281 if (type == IntrusionSensorType::pch)
282 {
283 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
284 << "\n";
285 }
286 else if (type == IntrusionSensorType::gpio)
287 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800288 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800289 << ", gpioInverted = " << gpioInverted << "\n";
290 }
291 }
292
293 if ((type == IntrusionSensorType::pch && busId == mBusId &&
294 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800295 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
296 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800297 {
298 return;
299 }
300
301 mType = type;
302 mBusId = busId;
303 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800304 mGpioInverted = gpioInverted;
305
306 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800307 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800308 {
309 // initialize first if not initialized before
310 if (!mInitialized)
311 {
312 mIface->register_property(
313 "Status", mValue,
314 [&](const std::string& req, std::string& propertyValue) {
315 return setSensorValue(req, propertyValue);
316 });
317 mIface->initialize();
318
319 if (mType == IntrusionSensorType::gpio)
320 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800321 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800322 }
323
324 mInitialized = true;
325 }
326
327 // start polling value
328 if (mType == IntrusionSensorType::pch)
329 {
330 pollSensorStatusByPch();
331 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800332 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800333 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800334 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800335 pollSensorStatusByGpio();
336 }
337 }
338
339 // invalid para, release resource
340 else
341 {
342 if (mInitialized)
343 {
344 if (mType == IntrusionSensorType::pch)
345 {
346 mPollTimer.cancel();
347 }
348 else if (mType == IntrusionSensorType::gpio)
349 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800350 mGpioFd.close();
351 if (mGpioLine)
352 {
353 mGpioLine.release();
354 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800355 }
356 mInitialized = false;
357 }
358 }
359}
360
361ChassisIntrusionSensor::ChassisIntrusionSensor(
362 boost::asio::io_service& io,
363 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500364 mIface(iface),
365 mType(IntrusionSensorType::gpio), mValue("unknown"), mOldValue("unknown"),
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800366 mBusId(-1), mSlaveAddr(-1), mPollTimer(io), mGpioInverted(false),
367 mGpioFd(io)
James Feist38fb5982020-05-28 10:09:54 -0700368{}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800369
370ChassisIntrusionSensor::~ChassisIntrusionSensor()
371{
372 if (mType == IntrusionSensorType::pch)
373 {
374 mPollTimer.cancel();
375 }
376 else if (mType == IntrusionSensorType::gpio)
377 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800378 mGpioFd.close();
379 if (mGpioLine)
380 {
381 mGpioLine.release();
382 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800383 }
384}