xref: /aosp_15_r20/external/pigweed/pw_spi_linux/spi_test.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_spi_linux/spi.h"
16 
17 #include <linux/spi/spidev.h>
18 
19 #include <array>
20 #include <vector>
21 
22 #include "pw_bytes/suffix.h"
23 #include "pw_span/span.h"
24 #include "pw_unit_test/framework.h"
25 
26 namespace pw::spi {
27 namespace {
28 
29 using ::pw::operator""_b;
30 
31 const pw::spi::Config kConfig = {.polarity = ClockPolarity::kActiveHigh,
32                                  .phase = ClockPhase::kFallingEdge,
33                                  .bits_per_word = BitsPerWord(8),
34                                  .bit_order = BitOrder::kMsbFirst};
35 const uint32_t kMaxSpeed = 2345678;
36 const int kFakeFd = 9999;
37 
SpanEq(ConstByteSpan span1,ConstByteSpan span2)38 bool SpanEq(ConstByteSpan span1, ConstByteSpan span2) {
39   if (span1.size() != span2.size()) {
40     return false;
41   }
42 
43   for (size_t i = 0; i < span1.size(); i++) {
44     if (span1[i] != span2[i]) {
45       return false;
46     }
47   }
48 
49   return true;
50 }
51 
52 //
53 // A mock ioctl() implementation which records requests and transfers.
54 //
55 std::vector<unsigned long> ioctl_requests;
56 std::vector<struct spi_ioc_transfer> ioctl_transfers;
57 
58 union spi_ioc_arg {
59   uint32_t u32;
60   struct spi_ioc_transfer* transfer;
61 };
62 
ioctl(int fd,unsigned long request,union spi_ioc_arg arg)63 extern "C" int ioctl(int fd, unsigned long request, union spi_ioc_arg arg) {
64   // Only the fake SPI fd is handled.
65   if (fd != kFakeFd) {
66     return -1;
67   }
68 
69   // Only "write" ioctls are mocked currently.
70   // Otherwise the caller would not get any result.
71   // (The rx_buf of SPI_IOC_MESSAGE is an exception.)
72   if (_IOC_DIR(request) != _IOC_WRITE) {
73     return -1;
74   }
75 
76   // Only SPI ioctls are mocked.
77   if (_IOC_TYPE(request) != SPI_IOC_MAGIC) {
78     return -1;
79   }
80 
81   // Record the ioctl request code.
82   ioctl_requests.push_back(request);
83 
84   // Record the individual transfers.
85   if (_IOC_NR(request) == _IOC_NR(SPI_IOC_MESSAGE(1))) {
86     if (_IOC_SIZE(request) % sizeof(struct spi_ioc_transfer) != 0) {
87       return -1;
88     }
89     int num_transfers = _IOC_SIZE(request) / sizeof(struct spi_ioc_transfer);
90     for (int i = 0; i < num_transfers; i++) {
91       struct spi_ioc_transfer& transfer = arg.transfer[i];
92       ioctl_transfers.push_back(transfer);
93       // TODO(jrreinhart): If desired, write some data to transfer.rx_buf.
94     }
95   }
96 
97   return 0;
98 }
99 
100 class LinuxSpiTest : public ::testing::Test {
101  public:
LinuxSpiTest()102   LinuxSpiTest() : initiator(kFakeFd, kMaxSpeed) {}
103 
104  protected:
SetUp()105   void SetUp() override {
106     ioctl_requests.clear();
107     ioctl_transfers.clear();
108   }
109 
110   LinuxInitiator initiator;
111 };
112 
113 //
114 // Tests
115 //
116 
TEST_F(LinuxSpiTest,ConfigureWorks)117 TEST_F(LinuxSpiTest, ConfigureWorks) {
118   PW_TEST_EXPECT_OK(initiator.Configure(kConfig));
119 
120   std::vector<unsigned long> expect{
121       SPI_IOC_WR_MODE32,
122       SPI_IOC_WR_LSB_FIRST,
123       SPI_IOC_WR_BITS_PER_WORD,
124       SPI_IOC_WR_MAX_SPEED_HZ,
125   };
126 
127   std::sort(ioctl_requests.begin(), ioctl_requests.end());
128   std::sort(expect.begin(), expect.end());
129   EXPECT_EQ(ioctl_requests, expect);
130 }
131 
TEST_F(LinuxSpiTest,NoReadOrWrite)132 TEST_F(LinuxSpiTest, NoReadOrWrite) {
133   EXPECT_EQ(initiator.WriteRead({}, {}), Status::InvalidArgument());
134 
135   EXPECT_EQ(ioctl_requests.size(), 0u);
136   EXPECT_EQ(ioctl_transfers.size(), 0u);
137 }
138 
TEST_F(LinuxSpiTest,WriteOnly)139 TEST_F(LinuxSpiTest, WriteOnly) {
140   // Write only
141   constexpr size_t kNumBytes = 4;
142   const std::array<std::byte, kNumBytes> write_buf = {1_b, 2_b, 3_b, 4_b};
143 
144   PW_TEST_EXPECT_OK(initiator.WriteRead(write_buf, {}));
145 
146   EXPECT_EQ(ioctl_requests.size(), 1u);
147   EXPECT_EQ(ioctl_transfers.size(), 1u);
148 
149   // Transfer 0: tx={1, 2, 3, 4}, rx==null
150   auto& xfer0 = ioctl_transfers[0];
151   EXPECT_EQ(xfer0.len, kNumBytes);
152   EXPECT_EQ(xfer0.rx_buf, 0u);
153   EXPECT_EQ(xfer0.tx_buf, reinterpret_cast<uintptr_t>(write_buf.data()));
154   ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
155   EXPECT_TRUE(SpanEq(xfer0_tx, write_buf));
156 }
157 
TEST_F(LinuxSpiTest,ReadOnly)158 TEST_F(LinuxSpiTest, ReadOnly) {
159   // Read only
160   constexpr size_t kNumBytes = 4;
161   std::array<std::byte, kNumBytes> read_buf;
162 
163   PW_TEST_EXPECT_OK(initiator.WriteRead({}, read_buf));
164 
165   EXPECT_EQ(ioctl_requests.size(), 1u);
166   EXPECT_EQ(ioctl_transfers.size(), 1u);
167 
168   // Transfer 0: tx==null, rx!=null
169   auto& xfer0 = ioctl_transfers[0];
170   EXPECT_EQ(xfer0.len, kNumBytes);
171   EXPECT_EQ(xfer0.rx_buf, reinterpret_cast<uintptr_t>(read_buf.data()));
172   EXPECT_EQ(xfer0.tx_buf, 0u);
173 }
174 
TEST_F(LinuxSpiTest,WriteReadEqualSize)175 TEST_F(LinuxSpiTest, WriteReadEqualSize) {
176   // Write = Read
177   constexpr size_t kNumBytes = 4;
178   const std::array<std::byte, kNumBytes> write_buf = {1_b, 2_b, 3_b, 4_b};
179   std::array<std::byte, kNumBytes> read_buf;
180 
181   PW_TEST_EXPECT_OK(initiator.WriteRead(write_buf, read_buf));
182 
183   EXPECT_EQ(ioctl_requests.size(), 1u);
184   EXPECT_EQ(ioctl_transfers.size(), 1u);
185 
186   // Transfer 0: Common tx={1, 2, 3, 4}, rx!=null
187   auto& xfer0 = ioctl_transfers[0];
188   EXPECT_EQ(xfer0.len, kNumBytes);
189   EXPECT_NE(xfer0.rx_buf, 0u);
190   EXPECT_NE(xfer0.tx_buf, 0u);
191   ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
192   EXPECT_TRUE(SpanEq(xfer0_tx, write_buf));
193 }
194 
TEST_F(LinuxSpiTest,WriteLargerThanReadSize)195 TEST_F(LinuxSpiTest, WriteLargerThanReadSize) {
196   // Write > Read
197   const std::array<std::byte, 5> write_buf = {1_b, 2_b, 3_b, 4_b, 5_b};
198   std::array<std::byte, 2> read_buf;
199 
200   PW_TEST_EXPECT_OK(initiator.WriteRead(write_buf, read_buf));
201 
202   EXPECT_EQ(ioctl_requests.size(), 1u);
203   EXPECT_EQ(ioctl_transfers.size(), 2u);  // split
204 
205   // Transfer 0: Common tx={1, 2}, rx!=null
206   auto& xfer0 = ioctl_transfers[0];
207   EXPECT_EQ(xfer0.len, 2u);
208   EXPECT_NE(xfer0.rx_buf, 0u);
209   EXPECT_NE(xfer0.tx_buf, 0u);
210   ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
211   EXPECT_TRUE(SpanEq(xfer0_tx, std::array{1_b, 2_b}));
212 
213   // Transfer 1: Remainder tx={3, 4, 5}, rx=null
214   auto& xfer1 = ioctl_transfers[1];
215   EXPECT_EQ(xfer1.len, 3u);
216   EXPECT_NE(xfer1.tx_buf, 0u);
217   EXPECT_EQ(xfer1.rx_buf, 0u);
218   ByteSpan xfer1_tx(reinterpret_cast<std::byte*>(xfer1.tx_buf), xfer1.len);
219   EXPECT_TRUE(SpanEq(xfer1_tx, std::array{3_b, 4_b, 5_b}));
220 }
221 
TEST_F(LinuxSpiTest,ReadLargerThanWriteSize)222 TEST_F(LinuxSpiTest, ReadLargerThanWriteSize) {
223   // Read > Write
224   const std::array<std::byte, 2> write_buf = {1_b, 2_b};
225   std::array<std::byte, 5> read_buf;
226 
227   PW_TEST_EXPECT_OK(initiator.WriteRead(write_buf, read_buf));
228 
229   EXPECT_EQ(ioctl_requests.size(), 1u);
230   EXPECT_EQ(ioctl_transfers.size(), 2u);  // split
231 
232   // Transfer 0: Common tx={1, 2}, rx!=null
233   auto& xfer0 = ioctl_transfers[0];
234   EXPECT_EQ(xfer0.len, 2u);
235   EXPECT_NE(xfer0.rx_buf, 0u);
236   EXPECT_NE(xfer0.tx_buf, 0u);
237   ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
238   EXPECT_TRUE(SpanEq(xfer0_tx, std::array{1_b, 2_b}));
239 
240   // Transfer 1: Remainder tx=null, rx!=null
241   auto& xfer1 = ioctl_transfers[1];
242   EXPECT_EQ(xfer1.len, 3u);
243   EXPECT_EQ(xfer1.tx_buf, 0u);
244   EXPECT_NE(xfer1.rx_buf, 0u);
245 }
246 
247 }  // namespace
248 }  // namespace pw::spi
249