• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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