capsules_extra/
pca9544a.rs
1use 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
45use 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 ReadControl(ControlField),
57
58 Done,
59}
60
61#[derive(Clone, Copy, PartialEq)]
62enum ControlField {
63 InterruptMask,
64 SelectedChannels,
65}
66
67mod upcall {
69 pub const CHANNEL_DONE: usize = 0;
72 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 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 buffer[0] = 0;
113
114 let mut index = 1;
116 for i in 0..4 {
117 if channel_bitmask & (0x01 << i) != 0 {
118 buffer[index] = i + 4;
120 index += 1;
121 }
122 }
123
124 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 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 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 return CommandReturn::success();
219 }
220 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 0 => CommandReturn::success(),
236
237 1 => self.select_channels(data as u8),
239
240 2 => self.select_channels(0),
242
243 3 => self.read_interrupts(),
245
246 4 => self.read_selected_channels(),
248
249 _ => 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}