GCC Code Coverage Report


Directory: ./
File: Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 0 101 0.0%
Functions: 0 4 0.0%
Branches: 0 79 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title LinuxSpiDriverImpl.cpp
3 // \author tcanham
4 // \brief cpp file for LinuxSpiDriver component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12
13 #include <fcntl.h>
14 #include <linux/spi/spidev.h>
15 #include <linux/types.h>
16 #include <sys/ioctl.h>
17 #include <unistd.h>
18 #include <Drv/LinuxSpiDriver/LinuxSpiDriverComponentImpl.hpp>
19 #include <Fw/FPrimeBasicTypes.hpp>
20 #include <Fw/Types/Assert.hpp>
21 #include <Fw/Types/FileNameString.hpp>
22 #include <Fw/Types/String.hpp>
23 #include <cerrno>
24 #include <cstdint>
25 #include <cstdio>
26 #include <cstdlib>
27 #include <cstring>
28
29 namespace Drv {
30
31 // ----------------------------------------------------------------------
32 // Handler implementations for user-defined typed input ports
33 // ----------------------------------------------------------------------
34
35 // @ DEPRECATED: Use SpiWriteRead port instead (same operation with a return value)
36 void LinuxSpiDriverComponentImpl::SpiReadWrite_handler(const FwIndexType portNum,
37 Fw::Buffer& writeBuffer,
38 Fw::Buffer& readBuffer) {
39 FW_ASSERT(portNum >= 0, static_cast<FwAssertArgType>(portNum));
40 FW_ASSERT(writeBuffer.isValid());
41 FW_ASSERT(readBuffer.isValid());
42 (void)SpiWriteRead_handler(portNum, writeBuffer, readBuffer);
43 }
44
45 SpiStatus LinuxSpiDriverComponentImpl::SpiWriteRead_handler(const FwIndexType portNum,
46 Fw::Buffer& writeBuffer,
47 Fw::Buffer& readBuffer) {
48 FW_ASSERT(portNum >= 0, static_cast<FwAssertArgType>(portNum));
49 FW_ASSERT(writeBuffer.isValid());
50 FW_ASSERT(readBuffer.isValid());
51 FW_ASSERT(writeBuffer.getSize() == readBuffer.getSize());
52
53 if (this->m_fd == -1) {
54 return SpiStatus::SPI_OPEN_ERR;
55 }
56
57 spi_ioc_transfer tr;
58 // Zero for unused fields:
59 (void)memset(&tr, 0, sizeof(tr));
60 tr.tx_buf = reinterpret_cast<__u64>(writeBuffer.getData());
61 tr.rx_buf = reinterpret_cast<__u64>(readBuffer.getData());
62 FW_ASSERT_NO_OVERFLOW(writeBuffer.getSize(), __u32);
63 tr.len = static_cast<__u32>(writeBuffer.getSize());
64 /*
65 .speed_hz = 0,
66 .delay_usecs = 0,
67 .bits_per_word = 0,
68 .cs_change = 0,
69 .tx_nbits = 0, // on more-recent kernel versions;
70 .rx_nbits = 0, // on more-recent kernel versions;
71 .pad = 0
72 */
73
74 int stat = ioctl(this->m_fd, SPI_IOC_MESSAGE(1), &tr);
75
76 if (stat < 1) {
77 this->log_WARNING_HI_SPI_WriteError(this->m_device, this->m_select, stat);
78 return SpiStatus::SPI_OTHER_ERR;
79 }
80 this->m_bytes += readBuffer.getSize();
81 this->tlmWrite_SPI_Bytes(this->m_bytes);
82
83 return SpiStatus::SPI_OK;
84 }
85
86 bool LinuxSpiDriverComponentImpl::open(FwIndexType device, FwIndexType select, SpiFrequency clock, SpiMode spiMode) {
87 FW_ASSERT(device >= 0, static_cast<FwAssertArgType>(device));
88 FW_ASSERT(select >= 0, static_cast<FwAssertArgType>(select));
89
90 this->m_device = device;
91 this->m_select = select;
92 int fd;
93 int ret;
94
95 // Open:
96 Fw::FileNameString devString;
97 Fw::FormatStatus formatStatus =
98 devString.format("/dev/spidev%" PRI_FwIndexType ".%" PRI_FwIndexType, device, select);
99 FW_ASSERT(formatStatus == Fw::FormatStatus::SUCCESS);
100
101 fd = ::open(devString.toChar(), O_RDWR);
102 if (fd == -1) {
103 this->log_WARNING_HI_SPI_OpenError(device, select, fd);
104 return false;
105 }
106
107 // Configure:
108 /*
109 * SPI Mode 0, 1, 2, 3
110 */
111
112 U8 mode; // Mode Select (CPOL = 0/1, CPHA = 0/1)
113 switch (spiMode) {
114 case SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW:
115 mode = SPI_MODE_0;
116 break;
117 case SpiMode::SPI_MODE_CPOL_LOW_CPHA_HIGH:
118 mode = SPI_MODE_1;
119 break;
120 case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_LOW:
121 mode = SPI_MODE_2;
122 break;
123 case SpiMode::SPI_MODE_CPOL_HIGH_CPHA_HIGH:
124 mode = SPI_MODE_3;
125 break;
126 default:
127 // Assert if the device SPI Mode is not in the correct range
128 FW_ASSERT(false, spiMode);
129 break;
130 }
131
132 ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
133 if (ret == -1) {
134 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
135 (void)::close(fd);
136 return false;
137 }
138
139 U8 read_mode = 0;
140 ret = ioctl(fd, SPI_IOC_RD_MODE, &read_mode);
141 if (ret == -1) {
142 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
143 (void)::close(fd);
144 return false;
145 }
146
147 if (mode != read_mode) {
148 this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("MODE"), mode, read_mode);
149 }
150
151 /*
152 * 8 bits per word
153 */
154 U8 bits = 8;
155 ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
156 if (ret == -1) {
157 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
158 (void)::close(fd);
159 return false;
160 }
161
162 U8 read_bits = 0;
163 ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &read_bits);
164 if (ret == -1) {
165 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
166 (void)::close(fd);
167 return false;
168 }
169
170 if (bits != read_bits) {
171 this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("BITS_PER_WORD"), bits, read_bits);
172 }
173
174 /*
175 * Max speed in Hz
176 */
177 ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &clock);
178 if (ret == -1) {
179 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
180 (void)::close(fd);
181 return false;
182 }
183
184 SpiFrequency read_clock;
185 ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &read_clock);
186 if (ret == -1) {
187 this->log_WARNING_HI_SPI_ConfigError(device, select, ret);
188 (void)::close(fd);
189 return false;
190 }
191
192 if (clock != read_clock) {
193 this->log_WARNING_LO_SPI_ConfigMismatch(device, select, Fw::String("MAX_SPEED_HZ"), clock, read_clock);
194 }
195
196 // The device is only published once it is fully configured, so that a failed configuration
197 // leaves the driver closed rather than pointing at a misconfigured device
198 this->m_fd = fd;
199
200 return true;
201 }
202
203 LinuxSpiDriverComponentImpl::~LinuxSpiDriverComponentImpl() {
204 (void)close(this->m_fd);
205 }
206
207 } // end namespace Drv
208