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 //
101 // Tests
102 //
103
TEST(LinuxSpiTest,ConfigureWorks)104 TEST(LinuxSpiTest, ConfigureWorks) {
105 Status status;
106
107 ioctl_requests.clear();
108
109 LinuxInitiator initiator(kFakeFd, kMaxSpeed);
110
111 status = initiator.Configure(kConfig);
112 EXPECT_TRUE(status.ok());
113
114 std::vector<unsigned long> expect{
115 SPI_IOC_WR_MODE32,
116 SPI_IOC_WR_LSB_FIRST,
117 SPI_IOC_WR_BITS_PER_WORD,
118 SPI_IOC_WR_MAX_SPEED_HZ,
119 };
120
121 std::sort(ioctl_requests.begin(), ioctl_requests.end());
122 std::sort(expect.begin(), expect.end());
123 EXPECT_EQ(ioctl_requests, expect);
124 }
125
TEST(LinuxSpiTest,WriteReadEqualSize)126 TEST(LinuxSpiTest, WriteReadEqualSize) {
127 ioctl_requests.clear();
128 ioctl_transfers.clear();
129
130 LinuxInitiator initiator(kFakeFd, kMaxSpeed);
131 Status status;
132
133 // Write = Read
134 constexpr size_t kNumBytes = 4;
135 std::array<std::byte, kNumBytes> write_buf = {1_b, 2_b, 3_b, 4_b};
136 std::array<std::byte, kNumBytes> read_buf;
137
138 status = initiator.WriteRead(write_buf, read_buf);
139 EXPECT_TRUE(status.ok());
140
141 EXPECT_EQ(ioctl_requests.size(), 1u);
142 EXPECT_EQ(ioctl_transfers.size(), 1u);
143
144 // Transfer 0: Common tx={1, 2, 3, 4}, rx!=null
145 auto& xfer0 = ioctl_transfers[0];
146 EXPECT_EQ(xfer0.len, kNumBytes);
147 EXPECT_NE(xfer0.rx_buf, 0u);
148 EXPECT_NE(xfer0.tx_buf, 0u);
149 ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
150 EXPECT_TRUE(SpanEq(xfer0_tx, write_buf));
151 }
152
TEST(LinuxSpiTest,WriteLargerThanReadSize)153 TEST(LinuxSpiTest, WriteLargerThanReadSize) {
154 ioctl_requests.clear();
155 ioctl_transfers.clear();
156
157 LinuxInitiator initiator(kFakeFd, kMaxSpeed);
158 Status status;
159
160 // Write > Read
161 std::array<std::byte, 5> write_buf = {1_b, 2_b, 3_b, 4_b, 5_b};
162 std::array<std::byte, 2> read_buf;
163
164 status = initiator.WriteRead(write_buf, read_buf);
165 EXPECT_TRUE(status.ok());
166
167 EXPECT_EQ(ioctl_requests.size(), 1u);
168 EXPECT_EQ(ioctl_transfers.size(), 2u); // split
169
170 // Transfer 0: Common tx={1, 2}, rx!=null
171 auto& xfer0 = ioctl_transfers[0];
172 EXPECT_EQ(xfer0.len, 2u);
173 EXPECT_NE(xfer0.rx_buf, 0u);
174 EXPECT_NE(xfer0.tx_buf, 0u);
175 ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
176 EXPECT_TRUE(SpanEq(xfer0_tx, std::array{1_b, 2_b}));
177
178 // Transfer 1: Remainder tx={3, 4, 5}, rx=null
179 auto& xfer1 = ioctl_transfers[1];
180 EXPECT_EQ(xfer1.len, 3u);
181 EXPECT_NE(xfer1.tx_buf, 0u);
182 EXPECT_EQ(xfer1.rx_buf, 0u);
183 ByteSpan xfer1_tx(reinterpret_cast<std::byte*>(xfer1.tx_buf), xfer1.len);
184 EXPECT_TRUE(SpanEq(xfer1_tx, std::array{3_b, 4_b, 5_b}));
185 }
186
TEST(LinuxSpiTest,ReadLargerThanWriteSize)187 TEST(LinuxSpiTest, ReadLargerThanWriteSize) {
188 ioctl_requests.clear();
189 ioctl_transfers.clear();
190
191 LinuxInitiator initiator(kFakeFd, kMaxSpeed);
192 Status status;
193
194 // Read > Write
195 std::array<std::byte, 2> write_buf = {1_b, 2_b};
196 std::array<std::byte, 5> read_buf;
197
198 status = initiator.WriteRead(write_buf, read_buf);
199 EXPECT_TRUE(status.ok());
200
201 EXPECT_EQ(ioctl_requests.size(), 1u);
202 EXPECT_EQ(ioctl_transfers.size(), 2u); // split
203
204 // Transfer 0: Common tx={1, 2}, rx!=null
205 auto& xfer0 = ioctl_transfers[0];
206 EXPECT_EQ(xfer0.len, 2u);
207 EXPECT_NE(xfer0.rx_buf, 0u);
208 EXPECT_NE(xfer0.tx_buf, 0u);
209 ByteSpan xfer0_tx(reinterpret_cast<std::byte*>(xfer0.tx_buf), xfer0.len);
210 EXPECT_TRUE(SpanEq(xfer0_tx, std::array{1_b, 2_b}));
211
212 // Transfer 1: Remainder tx=null, rx!=null
213 auto& xfer1 = ioctl_transfers[1];
214 EXPECT_EQ(xfer1.len, 3u);
215 EXPECT_EQ(xfer1.tx_buf, 0u);
216 EXPECT_NE(xfer1.rx_buf, 0u);
217 }
218
219 } // namespace
220 } // namespace pw::spi
221