1*cf77ef54SJagpal Singh Gill #include "device/device_factory.hpp"
2*cf77ef54SJagpal Singh Gill #include "modbus_server_tester.hpp"
3*cf77ef54SJagpal Singh Gill #include "port/base_port.hpp"
4*cf77ef54SJagpal Singh Gill
5*cf77ef54SJagpal Singh Gill #include <fcntl.h>
6*cf77ef54SJagpal Singh Gill
7*cf77ef54SJagpal Singh Gill #include <xyz/openbmc_project/Software/Version/client.hpp>
8*cf77ef54SJagpal Singh Gill
9*cf77ef54SJagpal Singh Gill #include <gtest/gtest.h>
10*cf77ef54SJagpal Singh Gill
11*cf77ef54SJagpal Singh Gill using namespace std::literals;
12*cf77ef54SJagpal Singh Gill using namespace testing;
13*cf77ef54SJagpal Singh Gill using SoftwareIntf =
14*cf77ef54SJagpal Singh Gill sdbusplus::client::xyz::openbmc_project::software::Version<>;
15*cf77ef54SJagpal Singh Gill
16*cf77ef54SJagpal Singh Gill namespace TestIntf = phosphor::modbus::test;
17*cf77ef54SJagpal Singh Gill namespace ModbusIntf = phosphor::modbus::rtu;
18*cf77ef54SJagpal Singh Gill namespace PortIntf = phosphor::modbus::rtu::port;
19*cf77ef54SJagpal Singh Gill namespace PortConfigIntf = PortIntf::config;
20*cf77ef54SJagpal Singh Gill namespace DeviceIntf = phosphor::modbus::rtu::device;
21*cf77ef54SJagpal Singh Gill namespace DeviceConfigIntf = DeviceIntf::config;
22*cf77ef54SJagpal Singh Gill
23*cf77ef54SJagpal Singh Gill class MockPort : public PortIntf::BasePort
24*cf77ef54SJagpal Singh Gill {
25*cf77ef54SJagpal Singh Gill public:
MockPort(sdbusplus::async::context & ctx,const PortConfigIntf::Config & config,const std::string & devicePath)26*cf77ef54SJagpal Singh Gill MockPort(sdbusplus::async::context& ctx,
27*cf77ef54SJagpal Singh Gill const PortConfigIntf::Config& config,
28*cf77ef54SJagpal Singh Gill const std::string& devicePath) : BasePort(ctx, config, devicePath)
29*cf77ef54SJagpal Singh Gill {}
30*cf77ef54SJagpal Singh Gill };
31*cf77ef54SJagpal Singh Gill
32*cf77ef54SJagpal Singh Gill class TestFirmware : public DeviceIntf::DeviceFirmware
33*cf77ef54SJagpal Singh Gill {
34*cf77ef54SJagpal Singh Gill public:
TestFirmware(sdbusplus::async::context & ctx,const DeviceConfigIntf::Config & config,PortIntf::BasePort & serialPort)35*cf77ef54SJagpal Singh Gill TestFirmware(sdbusplus::async::context& ctx,
36*cf77ef54SJagpal Singh Gill const DeviceConfigIntf::Config& config,
37*cf77ef54SJagpal Singh Gill PortIntf::BasePort& serialPort) :
38*cf77ef54SJagpal Singh Gill DeviceIntf::DeviceFirmware(ctx, config, serialPort)
39*cf77ef54SJagpal Singh Gill {}
40*cf77ef54SJagpal Singh Gill
getObjectPath()41*cf77ef54SJagpal Singh Gill auto getObjectPath() -> sdbusplus::message::object_path
42*cf77ef54SJagpal Singh Gill {
43*cf77ef54SJagpal Singh Gill return objectPath;
44*cf77ef54SJagpal Singh Gill }
45*cf77ef54SJagpal Singh Gill };
46*cf77ef54SJagpal Singh Gill
47*cf77ef54SJagpal Singh Gill class FirmwareTest : public ::testing::Test
48*cf77ef54SJagpal Singh Gill {
49*cf77ef54SJagpal Singh Gill public:
50*cf77ef54SJagpal Singh Gill PortConfigIntf::Config portConfig;
51*cf77ef54SJagpal Singh Gill static constexpr const char* clientDevicePath = "/tmp/ttyFirmwareTestPort0";
52*cf77ef54SJagpal Singh Gill static constexpr const char* serverDevicePath = "/tmp/ttyFirmwareTestPort1";
53*cf77ef54SJagpal Singh Gill static constexpr auto portName = "TestPort0";
54*cf77ef54SJagpal Singh Gill static constexpr auto baudRate = 115200;
55*cf77ef54SJagpal Singh Gill static constexpr const auto strBaudeRate = "b115200";
56*cf77ef54SJagpal Singh Gill std::string deviceName;
57*cf77ef54SJagpal Singh Gill std::string objectPath;
58*cf77ef54SJagpal Singh Gill static constexpr auto serviceName =
59*cf77ef54SJagpal Singh Gill "xyz.openbmc_project.TestModbusRTUFirmware";
60*cf77ef54SJagpal Singh Gill static constexpr auto firmwareName = "TestVersion";
61*cf77ef54SJagpal Singh Gill int socat_pid = -1;
62*cf77ef54SJagpal Singh Gill sdbusplus::async::context ctx;
63*cf77ef54SJagpal Singh Gill int fdClient = -1;
64*cf77ef54SJagpal Singh Gill std::unique_ptr<TestIntf::ServerTester> serverTester;
65*cf77ef54SJagpal Singh Gill int fdServer = -1;
66*cf77ef54SJagpal Singh Gill std::unique_ptr<MockPort> mockPort;
67*cf77ef54SJagpal Singh Gill
FirmwareTest()68*cf77ef54SJagpal Singh Gill FirmwareTest()
69*cf77ef54SJagpal Singh Gill {
70*cf77ef54SJagpal Singh Gill portConfig.name = portName;
71*cf77ef54SJagpal Singh Gill portConfig.portMode = PortConfigIntf::PortMode::rs485;
72*cf77ef54SJagpal Singh Gill portConfig.baudRate = baudRate;
73*cf77ef54SJagpal Singh Gill portConfig.rtsDelay = 1;
74*cf77ef54SJagpal Singh Gill
75*cf77ef54SJagpal Singh Gill deviceName = std::format("ResorviorPumpUnit_{}_{}",
76*cf77ef54SJagpal Singh Gill TestIntf::testDeviceAddress, portName);
77*cf77ef54SJagpal Singh Gill objectPath =
78*cf77ef54SJagpal Singh Gill std::format("{}/{}", SoftwareIntf::namespace_path, deviceName);
79*cf77ef54SJagpal Singh Gill
80*cf77ef54SJagpal Singh Gill std::string socatCmd = std::format(
81*cf77ef54SJagpal Singh Gill "socat -x -v -d -d pty,link={},rawer,echo=0,parenb,{} pty,link={},rawer,echo=0,parenb,{} & echo $!",
82*cf77ef54SJagpal Singh Gill serverDevicePath, strBaudeRate, clientDevicePath, strBaudeRate);
83*cf77ef54SJagpal Singh Gill
84*cf77ef54SJagpal Singh Gill // Start socat in the background and capture its PID
85*cf77ef54SJagpal Singh Gill FILE* fp = popen(socatCmd.c_str(), "r");
86*cf77ef54SJagpal Singh Gill EXPECT_NE(fp, nullptr) << "Failed to start socat: " << strerror(errno);
87*cf77ef54SJagpal Singh Gill EXPECT_GT(fscanf(fp, "%d", &socat_pid), 0);
88*cf77ef54SJagpal Singh Gill pclose(fp);
89*cf77ef54SJagpal Singh Gill
90*cf77ef54SJagpal Singh Gill // Wait for socat to start up
91*cf77ef54SJagpal Singh Gill sleep(1);
92*cf77ef54SJagpal Singh Gill
93*cf77ef54SJagpal Singh Gill fdClient = open(clientDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
94*cf77ef54SJagpal Singh Gill EXPECT_NE(fdClient, -1)
95*cf77ef54SJagpal Singh Gill << "Failed to open serial port " << clientDevicePath
96*cf77ef54SJagpal Singh Gill << " with error: " << strerror(errno);
97*cf77ef54SJagpal Singh Gill
98*cf77ef54SJagpal Singh Gill fdServer = open(serverDevicePath, O_RDWR | O_NOCTTY | O_NONBLOCK);
99*cf77ef54SJagpal Singh Gill EXPECT_NE(fdServer, -1)
100*cf77ef54SJagpal Singh Gill << "Failed to open serial port " << serverDevicePath
101*cf77ef54SJagpal Singh Gill << " with error: " << strerror(errno);
102*cf77ef54SJagpal Singh Gill
103*cf77ef54SJagpal Singh Gill ctx.request_name(serviceName);
104*cf77ef54SJagpal Singh Gill
105*cf77ef54SJagpal Singh Gill mockPort =
106*cf77ef54SJagpal Singh Gill std::make_unique<MockPort>(ctx, portConfig, clientDevicePath);
107*cf77ef54SJagpal Singh Gill
108*cf77ef54SJagpal Singh Gill serverTester = std::make_unique<TestIntf::ServerTester>(ctx, fdServer);
109*cf77ef54SJagpal Singh Gill }
110*cf77ef54SJagpal Singh Gill
~FirmwareTest()111*cf77ef54SJagpal Singh Gill ~FirmwareTest() noexcept override
112*cf77ef54SJagpal Singh Gill {
113*cf77ef54SJagpal Singh Gill if (fdClient != -1)
114*cf77ef54SJagpal Singh Gill {
115*cf77ef54SJagpal Singh Gill close(fdClient);
116*cf77ef54SJagpal Singh Gill fdClient = -1;
117*cf77ef54SJagpal Singh Gill }
118*cf77ef54SJagpal Singh Gill if (fdServer != -1)
119*cf77ef54SJagpal Singh Gill {
120*cf77ef54SJagpal Singh Gill close(fdServer);
121*cf77ef54SJagpal Singh Gill fdServer = -1;
122*cf77ef54SJagpal Singh Gill }
123*cf77ef54SJagpal Singh Gill kill(socat_pid, SIGTERM);
124*cf77ef54SJagpal Singh Gill }
125*cf77ef54SJagpal Singh Gill
testFirmwareVersion(std::string objectPath,DeviceConfigIntf::FirmwareRegister firmwareRegister,std::string expectedVersion)126*cf77ef54SJagpal Singh Gill auto testFirmwareVersion(
127*cf77ef54SJagpal Singh Gill std::string objectPath,
128*cf77ef54SJagpal Singh Gill DeviceConfigIntf::FirmwareRegister firmwareRegister,
129*cf77ef54SJagpal Singh Gill std::string expectedVersion) -> sdbusplus::async::task<void>
130*cf77ef54SJagpal Singh Gill {
131*cf77ef54SJagpal Singh Gill DeviceConfigIntf::DeviceFactoryConfig deviceFactoryConfig = {
132*cf77ef54SJagpal Singh Gill {
133*cf77ef54SJagpal Singh Gill .address = TestIntf::testDeviceAddress,
134*cf77ef54SJagpal Singh Gill .parity = ModbusIntf::Parity::none,
135*cf77ef54SJagpal Singh Gill .baudRate = baudRate,
136*cf77ef54SJagpal Singh Gill .name = deviceName,
137*cf77ef54SJagpal Singh Gill .portName = portConfig.name,
138*cf77ef54SJagpal Singh Gill .inventoryPath = sdbusplus::message::object_path(
139*cf77ef54SJagpal Singh Gill "xyz/openbmc_project/Inventory/ResorviorPumpUnit"),
140*cf77ef54SJagpal Singh Gill .sensorRegisters = {},
141*cf77ef54SJagpal Singh Gill .statusRegisters = {},
142*cf77ef54SJagpal Singh Gill .firmwareRegisters = {firmwareRegister},
143*cf77ef54SJagpal Singh Gill },
144*cf77ef54SJagpal Singh Gill DeviceConfigIntf::DeviceType::reservoirPumpUnit,
145*cf77ef54SJagpal Singh Gill DeviceConfigIntf::DeviceModel::RDF040DSS5193E0,
146*cf77ef54SJagpal Singh Gill };
147*cf77ef54SJagpal Singh Gill
148*cf77ef54SJagpal Singh Gill auto deviceFirmware =
149*cf77ef54SJagpal Singh Gill std::make_unique<TestFirmware>(ctx, deviceFactoryConfig, *mockPort);
150*cf77ef54SJagpal Singh Gill
151*cf77ef54SJagpal Singh Gill co_await deviceFirmware->readVersionRegister();
152*cf77ef54SJagpal Singh Gill
153*cf77ef54SJagpal Singh Gill EXPECT_TRUE(deviceFirmware->getObjectPath().str.starts_with(objectPath))
154*cf77ef54SJagpal Singh Gill << "Invalid ObjectPath";
155*cf77ef54SJagpal Singh Gill
156*cf77ef54SJagpal Singh Gill auto softwarePath = deviceFirmware->getObjectPath().str;
157*cf77ef54SJagpal Singh Gill
158*cf77ef54SJagpal Singh Gill auto properties = co_await SoftwareIntf(ctx)
159*cf77ef54SJagpal Singh Gill .service(serviceName)
160*cf77ef54SJagpal Singh Gill .path(softwarePath)
161*cf77ef54SJagpal Singh Gill .properties();
162*cf77ef54SJagpal Singh Gill
163*cf77ef54SJagpal Singh Gill EXPECT_EQ(properties.version, expectedVersion)
164*cf77ef54SJagpal Singh Gill << "Firmware version mismatch";
165*cf77ef54SJagpal Singh Gill
166*cf77ef54SJagpal Singh Gill co_return;
167*cf77ef54SJagpal Singh Gill }
168*cf77ef54SJagpal Singh Gill
SetUp()169*cf77ef54SJagpal Singh Gill void SetUp() override
170*cf77ef54SJagpal Singh Gill {
171*cf77ef54SJagpal Singh Gill // Process request to read firmware version
172*cf77ef54SJagpal Singh Gill ctx.spawn(serverTester->processRequests());
173*cf77ef54SJagpal Singh Gill }
174*cf77ef54SJagpal Singh Gill };
175*cf77ef54SJagpal Singh Gill
TEST_F(FirmwareTest,TestFirmwareVersion)176*cf77ef54SJagpal Singh Gill TEST_F(FirmwareTest, TestFirmwareVersion)
177*cf77ef54SJagpal Singh Gill {
178*cf77ef54SJagpal Singh Gill const DeviceConfigIntf::FirmwareRegister firmwareRegister = {
179*cf77ef54SJagpal Singh Gill .name = "",
180*cf77ef54SJagpal Singh Gill .type = DeviceConfigIntf::FirmwareRegisterType::version,
181*cf77ef54SJagpal Singh Gill .offset = TestIntf::testReadHoldingRegisterFirmwareVersionOffset,
182*cf77ef54SJagpal Singh Gill .size = TestIntf::testReadHoldingRegisterFirmwareVersionCount};
183*cf77ef54SJagpal Singh Gill
184*cf77ef54SJagpal Singh Gill ctx.spawn(testFirmwareVersion(
185*cf77ef54SJagpal Singh Gill objectPath, firmwareRegister,
186*cf77ef54SJagpal Singh Gill TestIntf::testReadHoldingRegisterFirmwareVersionStr));
187*cf77ef54SJagpal Singh Gill
188*cf77ef54SJagpal Singh Gill ctx.spawn(sdbusplus::async::sleep_for(ctx, 1s) |
189*cf77ef54SJagpal Singh Gill sdbusplus::async::execution::then([&]() { ctx.request_stop(); }));
190*cf77ef54SJagpal Singh Gill
191*cf77ef54SJagpal Singh Gill ctx.run();
192*cf77ef54SJagpal Singh Gill }
193