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