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}