capsules_core/
i2c_master.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//! SyscallDriver for an I2C Master interface.
6
7use enum_primitive::enum_from_primitive;
8
9use kernel::grant::{AllowRoCount, AllowRwCount, Grant, GrantKernelData, UpcallCount};
10use kernel::hil::i2c;
11use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
12use kernel::syscall::{CommandReturn, SyscallDriver};
13use kernel::utilities::cells::{MapCell, OptionalCell, TakeCell};
14use kernel::{ErrorCode, ProcessId};
15
16/// Syscall driver number.
17use crate::driver;
18pub const DRIVER_NUM: usize = driver::NUM::I2cMaster as usize;
19
20/// Ids for read-write allow buffers
21mod rw_allow {
22    pub const BUFFER: usize = 1;
23    /// The number of allow buffers the kernel stores for this grant
24    pub const COUNT: u8 = 2;
25}
26
27#[derive(Default)]
28pub struct App;
29
30pub const BUFFER_LENGTH: usize = 64;
31
32struct Transaction {
33    /// The buffer containing the bytes to transmit as it should be returned to
34    /// the client
35    processid: ProcessId,
36    /// The total amount to transmit
37    read_len: OptionalCell<usize>,
38}
39
40pub struct I2CMasterDriver<'a, I: i2c::I2CMaster<'a>> {
41    i2c: &'a I,
42    buf: TakeCell<'static, [u8]>,
43    tx: MapCell<Transaction>,
44    apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<{ rw_allow::COUNT }>>,
45}
46
47impl<'a, I: i2c::I2CMaster<'a>> I2CMasterDriver<'a, I> {
48    pub fn new(
49        i2c: &'a I,
50        buf: &'static mut [u8],
51        apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<{ rw_allow::COUNT }>>,
52    ) -> I2CMasterDriver<'a, I> {
53        I2CMasterDriver {
54            i2c,
55            buf: TakeCell::new(buf),
56            tx: MapCell::empty(),
57            apps,
58        }
59    }
60
61    fn operation(
62        &self,
63        processid: ProcessId,
64        kernel_data: &GrantKernelData,
65        command: Cmd,
66        addr: u8,
67        wlen: usize,
68        rlen: usize,
69    ) -> Result<(), ErrorCode> {
70        kernel_data
71            .get_readwrite_processbuffer(rw_allow::BUFFER)
72            .and_then(|buffer| {
73                buffer.enter(|app_buffer| {
74                    self.buf.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
75                        app_buffer[..wlen].copy_to_slice(&mut buffer[..wlen]);
76
77                        let read_len = if rlen == 0 {
78                            OptionalCell::empty()
79                        } else {
80                            OptionalCell::new(rlen)
81                        };
82                        self.tx.put(Transaction {
83                            processid,
84                            read_len,
85                        });
86
87                        let res = match command {
88                            Cmd::Ping => {
89                                self.buf.put(Some(buffer));
90                                return Err(ErrorCode::INVAL);
91                            }
92                            Cmd::Write => self.i2c.write(addr, buffer, wlen),
93                            Cmd::Read => self.i2c.read(addr, buffer, rlen),
94                            Cmd::WriteRead => self.i2c.write_read(addr, buffer, wlen, rlen),
95                        };
96                        match res {
97                            Ok(()) => Ok(()),
98                            Err((error, data)) => {
99                                self.buf.put(Some(data));
100                                Err(error.into())
101                            }
102                        }
103                    })
104                })
105            })
106            .unwrap_or(Err(ErrorCode::INVAL))
107    }
108}
109
110use enum_primitive::cast::FromPrimitive;
111
112enum_from_primitive! {
113#[derive(Debug, PartialEq, Clone, Copy)]
114pub enum Cmd {
115    Ping = 0,
116    Write = 1,
117    Read = 2,
118    WriteRead = 3,
119}
120}
121
122impl<'a, I: i2c::I2CMaster<'a>> SyscallDriver for I2CMasterDriver<'a, I> {
123    /// Setup shared buffers.
124    ///
125    /// ### `allow_num`
126    ///
127    /// - `1`: buffer for command
128
129    // Setup callbacks.
130    //
131    // ### `subscribe_num`
132    //
133    // - `0`: Write buffer completed callback
134
135    /// Initiate transfers
136    fn command(
137        &self,
138        cmd_num: usize,
139        arg1: usize,
140        arg2: usize,
141        processid: ProcessId,
142    ) -> CommandReturn {
143        if let Some(cmd) = Cmd::from_usize(cmd_num) {
144            match cmd {
145                Cmd::Ping => CommandReturn::success(),
146                Cmd::Write => self
147                    .apps
148                    .enter(processid, |_, kernel_data| {
149                        let addr = arg1 as u8;
150                        let write_len = arg2;
151                        self.operation(processid, kernel_data, Cmd::Write, addr, write_len, 0)
152                            .into()
153                    })
154                    .unwrap_or_else(|err| err.into()),
155                Cmd::Read => self
156                    .apps
157                    .enter(processid, |_, kernel_data| {
158                        let addr = arg1 as u8;
159                        let read_len = arg2;
160                        self.operation(processid, kernel_data, Cmd::Read, addr, 0, read_len)
161                            .into()
162                    })
163                    .unwrap_or_else(|err| err.into()),
164                Cmd::WriteRead => {
165                    let addr = arg1 as u8;
166                    let write_len = arg1 >> 8; // can extend to 24 bit write length
167                    let read_len = arg2; // can extend to 32 bit read length
168                    self.apps
169                        .enter(processid, |_, kernel_data| {
170                            self.operation(
171                                processid,
172                                kernel_data,
173                                Cmd::WriteRead,
174                                addr,
175                                write_len,
176                                read_len,
177                            )
178                            .into()
179                        })
180                        .unwrap_or_else(|err| err.into())
181                }
182            }
183        } else {
184            CommandReturn::failure(ErrorCode::NOSUPPORT)
185        }
186    }
187
188    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
189        self.apps.enter(processid, |_, _| {})
190    }
191}
192
193impl<'a, I: i2c::I2CMaster<'a>> i2c::I2CHwMasterClient for I2CMasterDriver<'a, I> {
194    fn command_complete(&self, buffer: &'static mut [u8], status: Result<(), i2c::Error>) {
195        self.tx.take().map(|tx| {
196            self.apps.enter(tx.processid, |_, kernel_data| {
197                if let Some(read_len) = tx.read_len.take() {
198                    let _ = kernel_data
199                        .get_readwrite_processbuffer(rw_allow::BUFFER)
200                        .and_then(|app_buffer| {
201                            app_buffer.mut_enter(|app_buffer| {
202                                app_buffer[..read_len].copy_from_slice(&buffer[..read_len]);
203                            })
204                        });
205                }
206
207                // signal to driver that tx complete
208                kernel_data
209                    .schedule_upcall(
210                        0,
211                        (
212                            kernel::errorcode::into_statuscode(status.map_err(|e| e.into())),
213                            0,
214                            0,
215                        ),
216                    )
217                    .ok();
218            })
219        });
220
221        //recover buffer
222        self.buf.put(Some(buffer));
223    }
224}