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}