F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
LinuxSpiDriverComponentImpl.cpp
Go to the documentation of this file.
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>
19 #include <Fw/FPrimeBasicTypes.hpp>
20 #include <Fw/Types/Assert.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) {
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);
79  }
80  this->m_bytes += readBuffer.getSize();
81  this->tlmWrite_SPI_Bytes(this->m_bytes);
82 
83  return SpiStatus::SPI_OK;
84 }
85 
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) {
115  mode = SPI_MODE_0;
116  break;
118  mode = SPI_MODE_1;
119  break;
121  mode = SPI_MODE_2;
122  break;
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 
204  (void)close(this->m_fd);
205 }
206 
207 } // end namespace Drv
bool open(FwIndexType device, FwIndexType select, SpiFrequency clock, SpiMode spiMode=SpiMode::SPI_MODE_CPOL_LOW_CPHA_LOW)
Open device.
void log_WARNING_HI_SPI_ConfigError(I32 device, I32 select, I32 error) const
void log_WARNING_HI_SPI_WriteError(I32 device, I32 select, I32 error)
U8 * getData() const
Definition: Buffer.cpp:56
void log_WARNING_LO_SPI_ConfigMismatch(I32 device, I32 select, const Fw::StringBase &parameter, U32 write_value, U32 read_value) const
void tlmWrite_SPI_Bytes(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) const
Other errors that do not fit.
bool isValid() const
Definition: Buffer.cpp:52
const char * toChar() const
Convert to a C-style char*.
#define PRI_FwIndexType
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:58
void log_WARNING_HI_SPI_OpenError(I32 device, I32 select, I32 error) const
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
FwSizeType getSize() const
Definition: Buffer.cpp:60
PlatformIndexType FwIndexType
#define FW_ASSERT_NO_OVERFLOW(value, T)
Definition: Assert.hpp:48
SPI driver failed to open device.
Transaction okay.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
FormatStatus
status of string format calls
Definition: format.hpp:18