Line data Source code
1 : /*
2 : * Copyright (C) 2021 Peter Barker. All rights reserved.
3 : *
4 : * This file is free software: you can redistribute it and/or modify it
5 : * under the terms of the GNU General Public License as published by the
6 : * Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This file is distributed in the hope that it will be useful, but
10 : * WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 : * See the GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License along
15 : * with this program. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "SPIDevice.h"
19 :
20 : #include <AP_HAL/AP_HAL.h>
21 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL && !defined(HAL_BUILD_AP_PERIPH)
22 :
23 : #include <SITL/SITL.h>
24 :
25 : extern const AP_HAL::HAL& hal;
26 :
27 : using namespace HALSITL;
28 :
29 : /*
30 : * SPIBus
31 : */
32 : class SPIBus {
33 : friend class SPIDeviceManager;
34 : public:
35 :
36 8 : SPIBus(uint8_t _bus) :
37 8 : bus{_bus}
38 8 : { }
39 :
40 : uint8_t bus;
41 : Semaphore sem;
42 263095 : int ioctl(uint8_t cs_pin, uint8_t ioctl_number, void *data) {
43 263095 : return _ioctl(cs_pin, ioctl_number, data);
44 : }
45 : // void _timer_tick(); // in lieu of a thread-per-bus
46 : AP_HAL::Device::PeriodicHandle register_periodic_callback(uint32_t period_usec, AP_HAL::Device::PeriodicCb cb);
47 :
48 : class SPIBus *next;
49 :
50 : private:
51 : int _ioctl(uint8_t cs_pin, uint8_t ioctl_number, void *data);
52 :
53 : struct callback_info {
54 : struct callback_info *next;
55 : AP_HAL::Device::PeriodicCb cb;
56 : uint32_t period_usec;
57 : uint64_t next_usec;
58 : } *callbacks;
59 :
60 : static uint8_t spi_buscount;
61 :
62 : };
63 :
64 : uint8_t SPIBus::spi_buscount;
65 :
66 : // FIXME: do the whole subdev dance here and get an actual FD to do an ioctl on?
67 263095 : int SPIBus::_ioctl(uint8_t cs_pin, uint8_t ioctl_number, void *data)
68 : {
69 263095 : SITL::SIM *sitl = AP::sitl();
70 263095 : return sitl->spi_ioctl(bus, cs_pin, ioctl_number, data);
71 : }
72 :
73 0 : AP_HAL::Device::PeriodicHandle SPIBus::register_periodic_callback(uint32_t period_usec, AP_HAL::Device::PeriodicCb cb)
74 : {
75 : // mostly swiped from ChibiOS:
76 0 : SPIBus::callback_info *callback = NEW_NOTHROW SPIBus::callback_info;
77 0 : if (callback == nullptr) {
78 0 : return nullptr;
79 : }
80 0 : callback->cb = cb;
81 0 : callback->period_usec = period_usec;
82 0 : callback->next_usec = AP_HAL::micros64() + period_usec;
83 :
84 : // add to linked list of callbacks on thread
85 0 : callback->next = callbacks;
86 0 : callbacks = callback;
87 0 : return callback;
88 : }
89 :
90 : // void SPIBus::_timer_tick()
91 : // {
92 : // const uint64_t now = AP_HAL::micros64();
93 : // for (struct callback_info *ci = callbacks; ci != nullptr; ci = ci->next) {
94 : // if (ci->next_usec < now) {
95 : // WITH_SEMAPHORE(sem);
96 : // ci->cb();
97 : // ci->next_usec += ci->period_usec;
98 : // }
99 : // }
100 : // }
101 :
102 : /*
103 : * SPIDeviceManager
104 : */
105 : SPIBus *SPIDeviceManager::buses;
106 :
107 2145 : SPIDeviceManager::SPIDeviceManager()
108 : {
109 2145 : }
110 :
111 : static const struct SPIDriverInfo {
112 : // SPIDriver *driver;
113 : uint8_t busid; // used for device IDs in parameters
114 : // uint8_t dma_channel_rx;
115 : // uint8_t dma_channel_tx;
116 : } spi_devices[] = {
117 : { 0, },
118 : { 1, },
119 : };
120 :
121 : // name, bus, cs_pin
122 : SPIDesc SPIDeviceManager::device_table[] = {
123 : { "ramtron", 0, 0 },
124 : { "dataflash", 1, 0}
125 : };
126 :
127 : AP_HAL::SPIDevice *
128 8 : SPIDeviceManager::get_device_ptr(const char *name)
129 : {
130 : // this was swiped from AP_HAL_ChibiOS
131 :
132 : /* Find the bus description in the table */
133 : uint8_t i;
134 13 : for (i = 0; i<ARRAY_SIZE(device_table); i++) {
135 13 : if (strcmp(device_table[i].name, name) == 0) {
136 8 : break;
137 : }
138 : }
139 8 : if (i == ARRAY_SIZE(device_table)) {
140 0 : return nullptr;
141 : }
142 :
143 8 : SPIDesc &desc = device_table[i];
144 :
145 : // find the bus
146 : SPIBus *busp;
147 8 : for (busp = buses; busp; busp = (SPIBus *)busp->next) {
148 0 : if (busp->bus == desc.bus) {
149 0 : break;
150 : }
151 : }
152 8 : if (busp == nullptr) {
153 : // create a new one
154 8 : busp = NEW_NOTHROW SPIBus(desc.bus);
155 8 : if (busp == nullptr) {
156 0 : return nullptr;
157 : }
158 8 : busp->next = buses;
159 8 : busp->bus = desc.bus;
160 :
161 8 : buses = busp;
162 : }
163 :
164 8 : return NEW_NOTHROW SPIDevice(*busp, desc);
165 : }
166 :
167 : // void SPIDeviceManager::_timer_tick()
168 : // {
169 : // for (auto &bus : buses) {
170 : // bus._timer_tick();
171 : // }
172 : // }
173 :
174 : /*
175 : * SPIDevice
176 : */
177 :
178 8 : SPIDevice::SPIDevice(SPIBus &_bus, SPIDesc &_device_desc)
179 8 : : bus(_bus)
180 8 : , device_desc(_device_desc)
181 : {
182 8 : if (_bus.bus >= ARRAY_SIZE(spi_devices)) {
183 0 : AP_HAL::panic("SPIDevice: bus %u out of range", (unsigned)_bus.bus);
184 : }
185 8 : set_device_bus(spi_devices[_bus.bus].busid);
186 8 : set_device_address(_device_desc.cs_pin);
187 8 : }
188 :
189 783 : AP_HAL::Semaphore *SPIDevice::get_semaphore()
190 : {
191 783 : return &bus.sem;
192 : }
193 :
194 : /*
195 : low level transfer function - swiped from the Linux HAL
196 : */
197 : // this is copied into the SITL SPI layer
198 : struct spi_ioc_transfer {
199 : uint64_t tx_buf;
200 : uint64_t rx_buf;
201 :
202 : uint32_t len;
203 : // uint32_t speed_hz;
204 :
205 : // uint16_t delay_usecs;
206 : // uint8_t bits_per_word;
207 : // uint8_t cs_change;
208 : };
209 :
210 : #define SPI_TRANSACTION_1LONG 17
211 : #define SPI_TRANSACTION_2LONG 18
212 :
213 263095 : bool SPIDevice::transfer(const uint8_t *send, uint32_t send_len,
214 : uint8_t *recv, uint32_t recv_len)
215 : {
216 263095 : bus.sem.check_owner();
217 :
218 263095 : struct spi_ioc_transfer msgs[2] = { };
219 263095 : unsigned nmsgs = 0;
220 :
221 263095 : if (send && send_len != 0) {
222 232035 : msgs[nmsgs].tx_buf = (uint64_t) send;
223 232035 : msgs[nmsgs].rx_buf = 0;
224 232035 : msgs[nmsgs].len = send_len;
225 232035 : nmsgs++;
226 : }
227 :
228 263095 : if (recv && recv_len != 0) {
229 104073 : msgs[nmsgs].tx_buf = 0;
230 104073 : msgs[nmsgs].rx_buf = (uint64_t) recv;
231 104073 : msgs[nmsgs].len = recv_len;
232 104073 : nmsgs++;
233 : }
234 :
235 263095 : if (!nmsgs) {
236 0 : return false;
237 : }
238 :
239 : uint8_t ioctl_number;
240 263095 : switch (nmsgs) {
241 190082 : case 1:
242 190082 : ioctl_number = SPI_TRANSACTION_1LONG;
243 190082 : break;
244 73013 : case 2:
245 73013 : ioctl_number = SPI_TRANSACTION_2LONG;
246 73013 : break;
247 0 : default:
248 0 : abort();
249 : }
250 263095 : const int r = bus.ioctl(device_desc.cs_pin, ioctl_number, &msgs);
251 :
252 263095 : if (r == -1) {
253 0 : hal.console->printf("SPIDevice: error transferring data\n");
254 0 : return false;
255 : }
256 :
257 263095 : return true;
258 : }
259 :
260 0 : AP_HAL::Device::PeriodicHandle SPIDevice::register_periodic_callback(uint32_t period_usec, AP_HAL::Device::PeriodicCb cb)
261 : {
262 0 : return bus.register_periodic_callback(period_usec, cb);
263 : }
264 :
265 : #endif // HAL_BOARD_SITL
|