blob: af5ced3bb8aa722fb5d250024296b9c4fa47508b [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
Qiang XUe28d1fa2019-02-27 13:50:56 +080017#include <fcntl.h>
18#include <sys/ioctl.h>
19#include <unistd.h>
20
Ed Tanous8a57ec02020-10-09 12:46:52 -070021#include <ChassisIntrusionSensor.hpp>
22#include <boost/asio/io_service.hpp>
James Feist38fb5982020-05-28 10:09:54 -070023#include <sdbusplus/asio/object_server.hpp>
24
Ed Tanous8a57ec02020-10-09 12:46:52 -070025#include <cerrno>
Qiang XUe28d1fa2019-02-27 13:50:56 +080026#include <chrono>
27#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <memory>
Qiang XUe28d1fa2019-02-27 13:50:56 +080029#include <string>
30#include <thread>
Ed Tanous8a57ec02020-10-09 12:46:52 -070031#include <utility>
Qiang XUe28d1fa2019-02-27 13:50:56 +080032
James Feist38fb5982020-05-28 10:09:54 -070033extern "C"
34{
Qiang XUe28d1fa2019-02-27 13:50:56 +080035#include <i2c/smbus.h>
36#include <linux/i2c-dev.h>
37}
38
Ed Tanous8a57ec02020-10-09 12:46:52 -070039static constexpr bool debug = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080040
41static constexpr unsigned int intrusionSensorPollSec = 1;
42
43// SMLink Status Register
44const static constexpr size_t pchStatusRegIntrusion = 0x04;
45
46// Status bit field masks
47const static constexpr size_t pchRegMaskIntrusion = 0x01;
48
Ed Tanous8a57ec02020-10-09 12:46:52 -070049void ChassisIntrusionSensor::updateValue(const std::string& newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +080050{
Josh Lehan833661a2020-03-04 17:35:41 -080051 // Take no action if value already equal
52 // Same semantics as Sensor::updateValue(const double&)
53 if (newValue == mValue)
54 {
55 return;
56 }
57
Qiang XUe28d1fa2019-02-27 13:50:56 +080058 // indicate that it is internal set call
59 mInternalSet = true;
60 mIface->set_property("Status", newValue);
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +053061 mInternalSet = false;
Qiang XUe28d1fa2019-02-27 13:50:56 +080062
63 mValue = newValue;
64
65 if (mOldValue == "Normal" && mValue != "Normal")
66 {
67 std::cerr << "save to SEL for intrusion assert event \n";
68 // TODO: call add SEL log API, depends on patch #13956
69 mOldValue = mValue;
70 }
71 else if (mOldValue != "Normal" && mValue == "Normal")
72 {
73 std::cerr << "save to SEL for intrusion de-assert event \n";
74 // TODO: call add SEL log API, depends on patch #13956
75 mOldValue = mValue;
76 }
77}
78
79int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
80{
81 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
82
83 int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
84 if (fd < 0)
85 {
86 std::cerr << "unable to open i2c device \n";
87 return -1;
88 }
89 if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
90 {
91 std::cerr << "unable to set device address\n";
92 close(fd);
93 return -1;
94 }
95
96 unsigned long funcs = 0;
97 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
98 {
99 std::cerr << "not support I2C_FUNCS \n";
100 close(fd);
101 return -1;
102 }
103
104 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
105 {
106 std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
107 close(fd);
108 return -1;
109 }
110
Ed Tanous8a57ec02020-10-09 12:46:52 -0700111 int32_t statusMask = pchRegMaskIntrusion;
112 int32_t statusReg = pchStatusRegIntrusion;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800113
Ed Tanous8a57ec02020-10-09 12:46:52 -0700114 int32_t statusValue = i2c_smbus_read_byte_data(fd, statusReg);
115 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800116 {
117 std::cout << "\nRead bus " << busId << " addr " << slaveAddr
118 << ", value = " << statusValue << "\n";
119 }
120
121 close(fd);
122
123 if (statusValue < 0)
124 {
125 std::cerr << "i2c_smbus_read_byte_data failed \n";
126 return -1;
127 }
128
129 // Get status value with mask
130 int newValue = statusValue & statusMask;
131
Ed Tanous8a57ec02020-10-09 12:46:52 -0700132 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800133 {
134 std::cout << "statusValue is " << statusValue << "\n";
135 std::cout << "Intrusion sensor value is " << newValue << "\n";
136 }
137
138 return newValue;
139}
140
141void ChassisIntrusionSensor::pollSensorStatusByPch()
142{
143 // setting a new experation implicitly cancels any pending async wait
144 mPollTimer.expires_from_now(
145 boost::posix_time::seconds(intrusionSensorPollSec));
146
147 mPollTimer.async_wait([&](const boost::system::error_code& ec) {
148 // case of timer expired
149 if (!ec)
150 {
151 int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
152 std::string newValue = statusValue ? "HardwareIntrusion" : "Normal";
153
Qiang XUe28d1fa2019-02-27 13:50:56 +0800154 if (newValue != "unknown" && mValue != newValue)
155 {
156 std::cout << "update value from " << mValue << " to "
157 << newValue << "\n";
158 updateValue(newValue);
159 }
160
161 // trigger next polling
162 pollSensorStatusByPch();
163 }
164 // case of being canceled
165 else if (ec == boost::asio::error::operation_aborted)
166 {
167 std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
168 return;
169 }
170 });
171}
172
173void ChassisIntrusionSensor::readGpio()
174{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800175 mGpioLine.event_read();
176 auto value = mGpioLine.get_value();
177
178 // set string defined in chassis redfish schema
179 std::string newValue = value ? "HardwareIntrusion" : "Normal";
180
Ed Tanous8a57ec02020-10-09 12:46:52 -0700181 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800182 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800183 std::cout << "\nGPIO value is " << value << "\n";
184 std::cout << "Intrusion sensor value is " << newValue << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800185 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800186
187 if (newValue != "unknown" && mValue != newValue)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800188 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800189 std::cout << "update value from " << mValue << " to " << newValue
190 << "\n";
191 updateValue(newValue);
Qiang XUe28d1fa2019-02-27 13:50:56 +0800192 }
193}
194
195void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
196{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800197 mGpioFd.async_wait(
198 boost::asio::posix::stream_descriptor::wait_read,
Qiang XUe28d1fa2019-02-27 13:50:56 +0800199 [this](const boost::system::error_code& ec) {
200 if (ec == boost::system::errc::bad_file_descriptor)
201 {
202 return; // we're being destroyed
203 }
Ed Tanous8a57ec02020-10-09 12:46:52 -0700204 if (ec)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800205 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800206 std::cerr
207 << "Error on GPIO based intrusion sensor wait event\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800208 }
209 else
210 {
211 readGpio();
212 }
213 pollSensorStatusByGpio();
214 });
215}
216
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800217void ChassisIntrusionSensor::initGpioDeviceFile()
Qiang XUe28d1fa2019-02-27 13:50:56 +0800218{
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800219 mGpioLine = gpiod::find_line(mPinName);
220 if (!mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800221 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800222 std::cerr << "ChassisIntrusionSensor error finding gpio pin name: "
223 << mPinName << "\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800224 return;
225 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800226
227 try
228 {
229
230 mGpioLine.request(
231 {"ChassisIntrusionSensor", gpiod::line_request::EVENT_BOTH_EDGES,
232 mGpioInverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
233
234 // set string defined in chassis redfish schema
235 auto value = mGpioLine.get_value();
236 std::string newValue = value ? "HardwareIntrusion" : "Normal";
237 updateValue(newValue);
238
239 auto gpioLineFd = mGpioLine.event_get_fd();
240 if (gpioLineFd < 0)
241 {
242 std::cerr << "ChassisIntrusionSensor failed to get " << mPinName
243 << " fd\n";
244 return;
245 }
246
247 mGpioFd.assign(gpioLineFd);
248 }
249 catch (std::system_error&)
250 {
251 std::cerr << "ChassisInrtusionSensor error requesting gpio pin name: "
252 << mPinName << "\n";
253 return;
254 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800255}
256
257int ChassisIntrusionSensor::setSensorValue(const std::string& req,
258 std::string& propertyValue)
259{
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530260 if (!mInternalSet)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800261 {
Qiang XUe28d1fa2019-02-27 13:50:56 +0800262 propertyValue = req;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800263 mOverridenState = true;
264 }
Richard Marian Thomaiyaraf6b87c2019-04-03 23:54:28 +0530265 else if (!mOverridenState)
266 {
267 propertyValue = req;
268 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800269 return 1;
270}
271
272void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800273 int slaveAddr, bool gpioInverted)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800274{
Ed Tanous8a57ec02020-10-09 12:46:52 -0700275 if (debug)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800276 {
277 std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
278 << "\n";
279 if (type == IntrusionSensorType::pch)
280 {
281 std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
282 << "\n";
283 }
284 else if (type == IntrusionSensorType::gpio)
285 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800286 std::cerr << "gpio pinName = " << mPinName
Qiang XUe28d1fa2019-02-27 13:50:56 +0800287 << ", gpioInverted = " << gpioInverted << "\n";
288 }
289 }
290
291 if ((type == IntrusionSensorType::pch && busId == mBusId &&
292 slaveAddr == mSlaveAddr) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800293 (type == IntrusionSensorType::gpio && gpioInverted == mGpioInverted &&
294 mInitialized))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800295 {
296 return;
297 }
298
299 mType = type;
300 mBusId = busId;
301 mSlaveAddr = slaveAddr;
Qiang XUe28d1fa2019-02-27 13:50:56 +0800302 mGpioInverted = gpioInverted;
303
304 if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800305 (mType == IntrusionSensorType::gpio))
Qiang XUe28d1fa2019-02-27 13:50:56 +0800306 {
307 // initialize first if not initialized before
308 if (!mInitialized)
309 {
310 mIface->register_property(
311 "Status", mValue,
312 [&](const std::string& req, std::string& propertyValue) {
313 return setSensorValue(req, propertyValue);
314 });
315 mIface->initialize();
316
317 if (mType == IntrusionSensorType::gpio)
318 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800319 initGpioDeviceFile();
Qiang XUe28d1fa2019-02-27 13:50:56 +0800320 }
321
322 mInitialized = true;
323 }
324
325 // start polling value
326 if (mType == IntrusionSensorType::pch)
327 {
328 pollSensorStatusByPch();
329 }
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800330 else if (mType == IntrusionSensorType::gpio && mGpioLine)
Qiang XUe28d1fa2019-02-27 13:50:56 +0800331 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800332 std::cerr << "Start polling intrusion sensors\n";
Qiang XUe28d1fa2019-02-27 13:50:56 +0800333 pollSensorStatusByGpio();
334 }
335 }
336
337 // invalid para, release resource
338 else
339 {
340 if (mInitialized)
341 {
342 if (mType == IntrusionSensorType::pch)
343 {
344 mPollTimer.cancel();
345 }
346 else if (mType == IntrusionSensorType::gpio)
347 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800348 mGpioFd.close();
349 if (mGpioLine)
350 {
351 mGpioLine.release();
352 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800353 }
354 mInitialized = false;
355 }
356 }
357}
358
359ChassisIntrusionSensor::ChassisIntrusionSensor(
360 boost::asio::io_service& io,
361 std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
Ed Tanous8a57ec02020-10-09 12:46:52 -0700362 mIface(std::move(iface)),
Brad Bishopfbb44ad2019-11-08 09:42:37 -0500363 mType(IntrusionSensorType::gpio), mValue("unknown"), mOldValue("unknown"),
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800364 mBusId(-1), mSlaveAddr(-1), mPollTimer(io), mGpioInverted(false),
365 mGpioFd(io)
James Feist38fb5982020-05-28 10:09:54 -0700366{}
Qiang XUe28d1fa2019-02-27 13:50:56 +0800367
368ChassisIntrusionSensor::~ChassisIntrusionSensor()
369{
370 if (mType == IntrusionSensorType::pch)
371 {
372 mPollTimer.cancel();
373 }
374 else if (mType == IntrusionSensorType::gpio)
375 {
ZhikuiRenba8a8bf2020-01-09 15:55:43 -0800376 mGpioFd.close();
377 if (mGpioLine)
378 {
379 mGpioLine.release();
380 }
Qiang XUe28d1fa2019-02-27 13:50:56 +0800381 }
382}