xref: /openbmc/phosphor-modbus/tests/test_firmware.cpp (revision cf77ef540b925e10e3078bbdfbd795a0c1d9ff1f)
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