capsules_extra/
pca9544a.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 the PCA9544A I2C Selector.
6//!
7//! This chip allows for multiple I2C devices with the same addresses to
8//! sit on the same I2C bus.
9//!
10//! <http://www.ti.com/product/PCA9544A>
11//!
12//! > The PCA9544A is a quad bidirectional translating switch controlled via the
13//! > I2C bus. The SCL/SDA upstream pair fans out to four downstream pairs, or
14//! > channels. One SCL/SDA pair can be selected at a time, and this is
15//! > determined by the contents of the programmable control register. Four
16//! > interrupt inputs (INT3–INT0), one for each of the downstream pairs, are
17//! > provided. One interrupt output (INT) acts as an AND of the four interrupt
18//! > inputs.
19//!
20//! Usage
21//! -----
22//!
23//! ```rust,ignore
24//! # use kernel::static_init;
25//!
26//! let pca9544a_i2c = static_init!(
27//!     capsules::virtual_i2c::I2CDevice,
28//!     capsules::virtual_i2c::I2CDevice::new(i2c_bus, 0x70));
29//! let pca9544a_buffer = static_init!([u8; capsules::pca9544a::BUFFER_LENGTH],
30//!                                    [0; capsules::pca9544a::BUFFER_LENGTH]);
31//! let pca9544a = static_init!(
32//!     capsules::pca9544a::PCA9544A<'static>,
33//!     capsules::pca9544a::PCA9544A::new(pca9544a_i2c, pca9544a_buffer));
34//! pca9544a_i2c.set_client(pca9544a);
35//! ```
36
37use core::cell::Cell;
38
39use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
40use kernel::hil::i2c;
41use kernel::syscall::{CommandReturn, SyscallDriver};
42use kernel::utilities::cells::{OptionalCell, TakeCell};
43use kernel::{ErrorCode, ProcessId};
44
45/// Syscall driver number.
46use capsules_core::driver;
47pub const DRIVER_NUM: usize = driver::NUM::Pca9544a as usize;
48
49pub const BUFFER_LENGTH: usize = 5;
50
51#[derive(Clone, Copy, PartialEq)]
52enum State {
53    Idle,
54
55    /// Read the control register and return the specified data field.
56    ReadControl(ControlField),
57
58    Done,
59}
60
61#[derive(Clone, Copy, PartialEq)]
62enum ControlField {
63    InterruptMask,
64    SelectedChannels,
65}
66
67/// IDs for subscribed upcalls.
68mod upcall {
69    /// Triggered when a channel is finished being selected or when the current
70    /// channel setup is returned.
71    pub const CHANNEL_DONE: usize = 0;
72    /// Number of upcalls.
73    pub const COUNT: u8 = 1;
74}
75
76#[derive(Default)]
77pub struct App {}
78
79pub struct PCA9544A<'a, I: i2c::I2CDevice> {
80    i2c: &'a I,
81    state: Cell<State>,
82    buffer: TakeCell<'static, [u8]>,
83    apps: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
84    owning_process: OptionalCell<ProcessId>,
85}
86
87impl<'a, I: i2c::I2CDevice> PCA9544A<'a, I> {
88    pub fn new(
89        i2c: &'a I,
90        buffer: &'static mut [u8],
91        grant: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
92    ) -> Self {
93        Self {
94            i2c,
95            state: Cell::new(State::Idle),
96            buffer: TakeCell::new(buffer),
97            apps: grant,
98            owning_process: OptionalCell::empty(),
99        }
100    }
101
102    /// Choose which channel(s) are active. Channels are encoded with a bitwise
103    /// mask (0x01 means enable channel 0, 0x0F means enable all channels).
104    /// Send 0 to disable all channels.
105    fn select_channels(&self, channel_bitmask: u8) -> CommandReturn {
106        self.buffer
107            .take()
108            .map_or(CommandReturn::failure(ErrorCode::NOMEM), |buffer| {
109                self.i2c.enable();
110
111                // Always clear the settings so we get to a known state
112                buffer[0] = 0;
113
114                // Iterate the bit array to send the correct channel enables
115                let mut index = 1;
116                for i in 0..4 {
117                    if channel_bitmask & (0x01 << i) != 0 {
118                        // B2 B1 B0 are set starting at 0x04
119                        buffer[index] = i + 4;
120                        index += 1;
121                    }
122                }
123
124                // TODO verify errors
125                let _ = self.i2c.write(buffer, index);
126                self.state.set(State::Done);
127
128                CommandReturn::success()
129            })
130    }
131
132    fn read_interrupts(&self) -> CommandReturn {
133        self.read_control(ControlField::InterruptMask)
134    }
135
136    fn read_selected_channels(&self) -> CommandReturn {
137        self.read_control(ControlField::SelectedChannels)
138    }
139
140    fn read_control(&self, field: ControlField) -> CommandReturn {
141        self.buffer
142            .take()
143            .map_or(CommandReturn::failure(ErrorCode::NOMEM), |buffer| {
144                self.i2c.enable();
145
146                // Just issuing a read to the selector reads its control register.
147                // TODO verify errors
148                let _ = self.i2c.read(buffer, 1);
149                self.state.set(State::ReadControl(field));
150
151                CommandReturn::success()
152            })
153    }
154}
155
156impl<I: i2c::I2CDevice> i2c::I2CClient for PCA9544A<'_, I> {
157    fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), i2c::Error>) {
158        match self.state.get() {
159            State::ReadControl(field) => {
160                let ret = match field {
161                    ControlField::InterruptMask => (buffer[0] >> 4) & 0x0F,
162                    ControlField::SelectedChannels => buffer[0] & 0x07,
163                };
164
165                self.owning_process.map(|pid| {
166                    let _ = self.apps.enter(pid, |_app, upcalls| {
167                        upcalls
168                            .schedule_upcall(
169                                upcall::CHANNEL_DONE,
170                                (field as usize + 1, ret as usize, 0),
171                            )
172                            .ok();
173                    });
174                });
175
176                self.buffer.replace(buffer);
177                self.i2c.disable();
178                self.state.set(State::Idle);
179            }
180            State::Done => {
181                self.owning_process.map(|pid| {
182                    let _ = self.apps.enter(pid, |_app, upcalls| {
183                        upcalls
184                            .schedule_upcall(upcall::CHANNEL_DONE, (0, 0, 0))
185                            .ok();
186                    });
187                });
188
189                self.buffer.replace(buffer);
190                self.i2c.disable();
191                self.state.set(State::Idle);
192            }
193            _ => {}
194        }
195    }
196}
197
198impl<I: i2c::I2CDevice> SyscallDriver for PCA9544A<'_, I> {
199    /// Control the I2C selector.
200    ///
201    /// ### `command_num`
202    ///
203    /// - `0`: Driver existence check.
204    /// - `1`: Choose which channels are active.
205    /// - `2`: Disable all channels.
206    /// - `3`: Read the list of fired interrupts.
207    /// - `4`: Read which channels are selected.
208    fn command(
209        &self,
210        command_num: usize,
211        data: usize,
212        _: usize,
213        process_id: ProcessId,
214    ) -> CommandReturn {
215        if command_num == 0 {
216            // Handle this first as it should be returned
217            // unconditionally
218            return CommandReturn::success();
219        }
220        // Check if this non-virtualized driver is already in use by
221        // some (alive) process
222        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
223            self.apps
224                .enter(current_process, |_, _| current_process == process_id)
225                .unwrap_or(true)
226        });
227        if match_or_empty_or_nonexistant {
228            self.owning_process.set(process_id);
229        } else {
230            return CommandReturn::failure(ErrorCode::NOMEM);
231        }
232
233        match command_num {
234            // Check if present.
235            0 => CommandReturn::success(),
236
237            // Select channels.
238            1 => self.select_channels(data as u8),
239
240            // Disable all channels.
241            2 => self.select_channels(0),
242
243            // Read the current interrupt fired mask.
244            3 => self.read_interrupts(),
245
246            // Read the current selected channels.
247            4 => self.read_selected_channels(),
248
249            // default
250            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
251        }
252    }
253
254    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
255        self.apps.enter(processid, |_, _| {})
256    }
257}