capsules_core/spi_peripheral.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Provides userspace applications with the ability to communicate over the SPI
6//! bus as a peripheral. Only supports chip select 0.
7
8use core::cell::Cell;
9use core::cmp;
10
11use kernel::grant::{AllowRoCount, AllowRwCount, Grant, GrantKernelData, UpcallCount};
12use kernel::hil::spi::ClockPhase;
13use kernel::hil::spi::ClockPolarity;
14use kernel::hil::spi::{SpiSlaveClient, SpiSlaveDevice};
15use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
16use kernel::syscall::{CommandReturn, SyscallDriver};
17use kernel::utilities::cells::{OptionalCell, TakeCell};
18use kernel::{ErrorCode, ProcessId};
19
20/// Syscall driver number.
21use crate::driver;
22pub const DRIVER_NUM: usize = driver::NUM::SpiPeripheral as usize;
23
24/// Ids for read-only allow buffers
25mod ro_allow {
26 pub const WRITE: usize = 0;
27 /// The number of allow buffers the kernel stores for this grant
28 pub const COUNT: u8 = 1;
29}
30
31/// Ids for read-write allow buffers
32mod rw_allow {
33 pub const READ: usize = 0;
34 /// The number of allow buffers the kernel stores for this grant
35 pub const COUNT: u8 = 1;
36}
37
38/// Suggested length for the SPI read and write buffer
39pub const DEFAULT_READ_BUF_LENGTH: usize = 1024;
40pub const DEFAULT_WRITE_BUF_LENGTH: usize = 1024;
41
42// Since we provide an additional callback in slave mode for
43// when the chip is selected, we have added a "PeripheralApp" struct
44// that includes this new callback field.
45#[derive(Default)]
46pub struct PeripheralApp {
47 len: usize,
48 index: usize,
49}
50
51pub struct SpiPeripheral<'a, S: SpiSlaveDevice<'a>> {
52 spi_slave: &'a S,
53 busy: Cell<bool>,
54 kernel_read: TakeCell<'static, [u8]>,
55 kernel_write: TakeCell<'static, [u8]>,
56 kernel_len: Cell<usize>,
57 grants: Grant<
58 PeripheralApp,
59 UpcallCount<2>,
60 AllowRoCount<{ ro_allow::COUNT }>,
61 AllowRwCount<{ rw_allow::COUNT }>,
62 >,
63 current_process: OptionalCell<ProcessId>,
64}
65
66impl<'a, S: SpiSlaveDevice<'a>> SpiPeripheral<'a, S> {
67 pub fn new(
68 spi_slave: &'a S,
69 grants: Grant<
70 PeripheralApp,
71 UpcallCount<2>,
72 AllowRoCount<{ ro_allow::COUNT }>,
73 AllowRwCount<{ rw_allow::COUNT }>,
74 >,
75 ) -> SpiPeripheral<'a, S> {
76 SpiPeripheral {
77 spi_slave,
78 busy: Cell::new(false),
79 kernel_len: Cell::new(0),
80 kernel_read: TakeCell::empty(),
81 kernel_write: TakeCell::empty(),
82 grants,
83 current_process: OptionalCell::empty(),
84 }
85 }
86
87 pub fn config_buffers(&self, read: &'static mut [u8], write: &'static mut [u8]) {
88 let len = cmp::min(read.len(), write.len());
89 self.kernel_len.set(len);
90 self.kernel_read.replace(read);
91 self.kernel_write.replace(write);
92 }
93
94 // Assumes checks for busy/etc. already done
95 // Updates app.index to be index + length of op
96 fn do_next_read_write(&self, app: &mut PeripheralApp, kernel_data: &GrantKernelData) {
97 let write_len = self.kernel_write.map_or(0, |kwbuf| {
98 let mut start = app.index;
99 let tmp_len = kernel_data
100 .get_readonly_processbuffer(ro_allow::WRITE)
101 .and_then(|write| {
102 write.enter(|src| {
103 let len = cmp::min(app.len - start, self.kernel_len.get());
104 let end = cmp::min(start + len, src.len());
105 start = cmp::min(start, end);
106
107 for (i, c) in src[start..end].iter().enumerate() {
108 kwbuf[i] = c.get();
109 }
110 end - start
111 })
112 })
113 .unwrap_or(0);
114 app.index = start + tmp_len;
115 tmp_len
116 });
117 // TODO verify SPI return value
118 let _ = self.spi_slave.read_write_bytes(
119 self.kernel_write.take(),
120 self.kernel_read.take(),
121 write_len,
122 );
123 }
124}
125
126impl<'a, S: SpiSlaveDevice<'a>> SyscallDriver for SpiPeripheral<'a, S> {
127 /// Provide read/write buffers to SpiPeripheral
128 ///
129 /// - allow_num 0: Provides a buffer to receive transfers into.
130
131 /// Provide read-only buffers to SpiPeripheral
132 ///
133 /// - allow_num 0: Provides a buffer to transmit
134
135 /// - 0: driver existence check
136 /// - 1: read/write buffers
137 /// - read and write buffers optional
138 /// - fails if arg1 (bytes to write) >
139 /// write_buffer.len()
140 /// - 2: get chip select
141 /// - returns current selected peripheral
142 /// - in slave mode, always returns 0
143 /// - 3: set clock phase on current peripheral
144 /// - 0 is sample leading
145 /// - non-zero is sample trailing
146 /// - 4: get clock phase on current peripheral
147 /// - 0 is sample leading
148 /// - non-zero is sample trailing
149 /// - 5: set clock polarity on current peripheral
150 /// - 0 is idle low
151 /// - non-zero is idle high
152 /// - 6: get clock polarity on current peripheral
153 /// - 0 is idle low
154 /// - non-zero is idle high
155 /// - x: lock spi
156 /// - if you perform an operation without the lock,
157 /// it implicitly acquires the lock before the
158 /// operation and releases it after
159 /// - while an app holds the lock no other app can issue
160 /// operations on SPI (they are buffered)
161 /// - not implemented or currently supported
162 /// - x+1: unlock spi
163 /// - does nothing if lock not held
164 /// - not implemented or currently supported
165 fn command(
166 &self,
167 command_num: usize,
168 arg1: usize,
169 _: usize,
170 process_id: ProcessId,
171 ) -> CommandReturn {
172 if command_num == 0 {
173 // Handle unconditional driver existence check.
174 return CommandReturn::success();
175 }
176
177 // Check if this driver is free, or already dedicated to this process.
178 let match_or_empty_or_nonexistant = self.current_process.map_or(true, |current_process| {
179 self.grants
180 .enter(current_process, |_, _| current_process == process_id)
181 .unwrap_or(true)
182 });
183 if match_or_empty_or_nonexistant {
184 self.current_process.set(process_id);
185 } else {
186 return CommandReturn::failure(ErrorCode::NOMEM);
187 }
188
189 match command_num {
190 1 => {
191 // read_write_bytes
192 if self.busy.get() {
193 return CommandReturn::failure(ErrorCode::BUSY);
194 }
195 self.grants
196 .enter(process_id, |app, kernel_data| {
197 // When we do a read/write, the read part is optional.
198 // So there are three cases:
199 // 1) Write and read buffers present: len is min of lengths
200 // 2) Only write buffer present: len is len of write
201 // 3) No write buffer present: no operation
202 let wlen = kernel_data
203 .get_readonly_processbuffer(ro_allow::WRITE)
204 .map_or(0, |write| write.len());
205 let rlen = kernel_data
206 .get_readwrite_processbuffer(rw_allow::READ)
207 .map_or(0, |read| read.len());
208 // Note that non-shared and 0-sized read buffers both report 0 as size
209 let len = if rlen == 0 { wlen } else { wlen.min(rlen) };
210
211 if len >= arg1 && arg1 > 0 {
212 app.len = arg1;
213 app.index = 0;
214 self.busy.set(true);
215 self.do_next_read_write(app, kernel_data);
216 CommandReturn::success()
217 } else {
218 /* write buffer too small, or zero length write */
219 CommandReturn::failure(ErrorCode::INVAL)
220 }
221 })
222 .unwrap_or(CommandReturn::failure(ErrorCode::NOMEM))
223 }
224 2 => {
225 // get chip select
226 // Only 0 is supported
227 CommandReturn::success_u32(0)
228 }
229 3 => {
230 // set phase
231 match match arg1 {
232 0 => self.spi_slave.set_phase(ClockPhase::SampleLeading),
233 _ => self.spi_slave.set_phase(ClockPhase::SampleTrailing),
234 } {
235 Ok(()) => CommandReturn::success(),
236 Err(error) => CommandReturn::failure(error),
237 }
238 }
239 4 => {
240 // get phase
241 CommandReturn::success_u32(self.spi_slave.get_phase() as u32)
242 }
243 5 => {
244 // set polarity
245 match match arg1 {
246 0 => self.spi_slave.set_polarity(ClockPolarity::IdleLow),
247 _ => self.spi_slave.set_polarity(ClockPolarity::IdleHigh),
248 } {
249 Ok(()) => CommandReturn::success(),
250 Err(error) => CommandReturn::failure(error),
251 }
252 }
253 6 => {
254 // get polarity
255 CommandReturn::success_u32(self.spi_slave.get_polarity() as u32)
256 }
257 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
258 }
259 }
260
261 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
262 self.grants.enter(processid, |_, _| {})
263 }
264}
265
266impl<'a, S: SpiSlaveDevice<'a>> SpiSlaveClient for SpiPeripheral<'a, S> {
267 fn read_write_done(
268 &self,
269 writebuf: Option<&'static mut [u8]>,
270 readbuf: Option<&'static mut [u8]>,
271 length: usize,
272 _status: Result<(), ErrorCode>,
273 ) {
274 self.current_process.map(|process_id| {
275 let _ = self.grants.enter(process_id, move |app, kernel_data| {
276 let rbuf = readbuf.inspect(|src| {
277 let index = app.index;
278 let _ = kernel_data
279 .get_readwrite_processbuffer(rw_allow::READ)
280 .and_then(|read| {
281 read.mut_enter(|dest| {
282 // Need to be careful that app_read hasn't changed
283 // under us, so check all values against actual
284 // slice lengths.
285 //
286 // If app_read is shorter than before, and shorter
287 // than what we have read would require, then truncate.
288 // -pal 12/9/20
289 let end = index;
290 let start = index - length;
291 let end = cmp::min(end, cmp::min(src.len(), dest.len()));
292
293 // If the new endpoint is earlier than our expected
294 // startpoint, we set the startpoint to be the same;
295 // This results in a zero-length operation. -pal 12/9/20
296 let start = cmp::min(start, end);
297
298 let dest_area = &dest[start..end];
299 let real_len = end - start;
300
301 for (i, c) in src[0..real_len].iter().enumerate() {
302 dest_area[i].set(*c);
303 }
304 })
305 });
306 });
307
308 self.kernel_read.put(rbuf);
309 self.kernel_write.put(writebuf);
310
311 if app.index == app.len {
312 self.busy.set(false);
313 let len = app.len;
314 app.len = 0;
315 app.index = 0;
316 kernel_data.schedule_upcall(0, (len, 0, 0)).ok();
317 } else {
318 self.do_next_read_write(app, kernel_data);
319 }
320 });
321 });
322 }
323
324 // Simple callback for when chip has been selected
325 fn chip_selected(&self) {
326 self.current_process.map(|process_id| {
327 let _ = self.grants.enter(process_id, move |app, kernel_data| {
328 let len = app.len;
329 kernel_data.schedule_upcall(1, (len, 0, 0)).ok();
330 });
331 });
332 }
333}