blob: 680d9e653608d6986f9e68a1ac1fab2201bcae6a [file] [log] [blame]
/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include "ChassisIntrusionSensor.hpp"
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <chrono>
#include <iostream>
#include <sdbusplus/asio/object_server.hpp>
#include <string>
#include <thread>
extern "C" {
#include <i2c/smbus.h>
#include <linux/i2c-dev.h>
}
static constexpr bool DEBUG = false;
static constexpr unsigned int intrusionSensorPollSec = 1;
// SMLink Status Register
const static constexpr size_t pchStatusRegIntrusion = 0x04;
// Status bit field masks
const static constexpr size_t pchRegMaskIntrusion = 0x01;
// gpio sysfs path
constexpr const char* gpioPath = "/sys/class/gpio/";
void ChassisIntrusionSensor::updateValue(const std::string newValue)
{
// indicate that it is internal set call
mInternalSet = true;
mIface->set_property("Status", newValue);
mInternalSet = false;
mValue = newValue;
if (mOldValue == "Normal" && mValue != "Normal")
{
std::cerr << "save to SEL for intrusion assert event \n";
// TODO: call add SEL log API, depends on patch #13956
mOldValue = mValue;
}
else if (mOldValue != "Normal" && mValue == "Normal")
{
std::cerr << "save to SEL for intrusion de-assert event \n";
// TODO: call add SEL log API, depends on patch #13956
mOldValue = mValue;
}
}
int ChassisIntrusionSensor::i2cReadFromPch(int busId, int slaveAddr)
{
std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
int fd = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
if (fd < 0)
{
std::cerr << "unable to open i2c device \n";
return -1;
}
if (ioctl(fd, I2C_SLAVE_FORCE, slaveAddr) < 0)
{
std::cerr << "unable to set device address\n";
close(fd);
return -1;
}
unsigned long funcs = 0;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
{
std::cerr << "not support I2C_FUNCS \n";
close(fd);
return -1;
}
if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
{
std::cerr << "not support I2C_FUNC_SMBUS_READ_BYTE_DATA \n";
close(fd);
return -1;
}
int statusValue;
unsigned int statusMask = pchRegMaskIntrusion;
unsigned int statusReg = pchStatusRegIntrusion;
statusValue = i2c_smbus_read_byte_data(fd, statusReg);
if (DEBUG)
{
std::cout << "\nRead bus " << busId << " addr " << slaveAddr
<< ", value = " << statusValue << "\n";
}
close(fd);
if (statusValue < 0)
{
std::cerr << "i2c_smbus_read_byte_data failed \n";
return -1;
}
// Get status value with mask
int newValue = statusValue & statusMask;
if (DEBUG)
{
std::cout << "statusValue is " << statusValue << "\n";
std::cout << "Intrusion sensor value is " << newValue << "\n";
}
return newValue;
}
void ChassisIntrusionSensor::pollSensorStatusByPch()
{
// setting a new experation implicitly cancels any pending async wait
mPollTimer.expires_from_now(
boost::posix_time::seconds(intrusionSensorPollSec));
mPollTimer.async_wait([&](const boost::system::error_code& ec) {
// case of timer expired
if (!ec)
{
int statusValue = i2cReadFromPch(mBusId, mSlaveAddr);
std::string newValue = statusValue ? "HardwareIntrusion" : "Normal";
if (newValue != "unknown" && mValue != newValue)
{
std::cout << "update value from " << mValue << " to "
<< newValue << "\n";
updateValue(newValue);
}
// trigger next polling
pollSensorStatusByPch();
}
// case of being canceled
else if (ec == boost::asio::error::operation_aborted)
{
std::cerr << "Timer of intrusion sensor is cancelled. Return \n";
return;
}
});
}
void ChassisIntrusionSensor::readGpio()
{
constexpr size_t readSize = sizeof("0");
std::string readBuf;
readBuf.resize(readSize);
lseek(mFd, 0, SEEK_SET);
size_t r = ::read(mFd, readBuf.data(), readSize);
if (r != readSize)
{
std::cerr << "Error reading gpio\n";
}
else
{
bool value = std::stoi(readBuf);
if (mGpioInverted)
{
value = !value;
}
// set string defined in chassis redfish schema
std::string newValue = value ? "HardwareIntrusion" : "Normal";
if (DEBUG)
{
std::cout << "\nGPIO value is " << value << "\n";
std::cout << "Intrusion sensor value is " << newValue << "\n";
}
if (newValue != "unknown" && mValue != newValue)
{
std::cout << "update value from " << mValue << " to " << newValue
<< "\n";
updateValue(newValue);
}
}
}
void ChassisIntrusionSensor::pollSensorStatusByGpio(void)
{
mInputDev.async_wait(
boost::asio::ip::tcp::socket::wait_error,
[this](const boost::system::error_code& ec) {
if (ec == boost::system::errc::bad_file_descriptor)
{
return; // we're being destroyed
}
else if (ec)
{
std::cerr << "Error on GPIO based intrusion sensor socket\n";
}
else
{
readGpio();
}
pollSensorStatusByGpio();
});
}
void ChassisIntrusionSensor::initGpioDeviceFile(const int index)
{
std::string device = gpioPath + std::string("gpio") + std::to_string(index);
mFd = open((device + "/value").c_str(), O_RDONLY);
if (mFd < 0)
{
std::cerr << "Error opening gpio " << index << "\n";
return;
}
mInputDev.assign(boost::asio::ip::tcp::v4(), mFd);
}
int ChassisIntrusionSensor::setSensorValue(const std::string& req,
std::string& propertyValue)
{
if (!mInternalSet)
{
propertyValue = req;
mOverridenState = true;
}
else if (!mOverridenState)
{
propertyValue = req;
}
return 1;
}
void ChassisIntrusionSensor::start(IntrusionSensorType type, int busId,
int slaveAddr, int gpioIndex,
bool gpioInverted)
{
if (DEBUG)
{
std::cerr << "enter ChassisIntrusionSensor::start, type = " << type
<< "\n";
if (type == IntrusionSensorType::pch)
{
std::cerr << "busId = " << busId << ", slaveAddr = " << slaveAddr
<< "\n";
}
else if (type == IntrusionSensorType::gpio)
{
std::cerr << "gpioIndex = " << gpioIndex
<< ", gpioInverted = " << gpioInverted << "\n";
}
}
if ((type == IntrusionSensorType::pch && busId == mBusId &&
slaveAddr == mSlaveAddr) ||
(type == IntrusionSensorType::gpio && gpioIndex == mGpioIndex &&
gpioInverted == mGpioInverted))
{
return;
}
mType = type;
mBusId = busId;
mSlaveAddr = slaveAddr;
mGpioIndex = gpioIndex;
mGpioInverted = gpioInverted;
if ((mType == IntrusionSensorType::pch && mBusId > 0 && mSlaveAddr > 0) ||
(mType == IntrusionSensorType::gpio && mGpioIndex > 0))
{
// initialize first if not initialized before
if (!mInitialized)
{
mIface->register_property(
"Status", mValue,
[&](const std::string& req, std::string& propertyValue) {
return setSensorValue(req, propertyValue);
});
mIface->initialize();
if (mType == IntrusionSensorType::gpio)
{
initGpioDeviceFile(mGpioIndex);
}
mInitialized = true;
}
// start polling value
if (mType == IntrusionSensorType::pch)
{
pollSensorStatusByPch();
}
else if (mType == IntrusionSensorType::gpio && mFd > 0)
{
pollSensorStatusByGpio();
}
}
// invalid para, release resource
else
{
if (mInitialized)
{
if (mType == IntrusionSensorType::pch)
{
mPollTimer.cancel();
}
else if (mType == IntrusionSensorType::gpio)
{
mInputDev.close();
close(mFd);
}
mInitialized = false;
}
}
}
ChassisIntrusionSensor::ChassisIntrusionSensor(
boost::asio::io_service& io,
std::shared_ptr<sdbusplus::asio::dbus_interface> iface) :
mPollTimer(io),
mIface(iface), mInputDev(io), mType(IntrusionSensorType::gpio), mBusId(-1),
mSlaveAddr(-1), mGpioIndex(-1), mGpioInverted(false), mValue("unknown"),
mOldValue("unknown")
{
}
ChassisIntrusionSensor::~ChassisIntrusionSensor()
{
if (mType == IntrusionSensorType::pch)
{
mPollTimer.cancel();
}
else if (mType == IntrusionSensorType::gpio)
{
mInputDev.close();
close(mFd);
}
}