capsules_extra/
ninedof.rs1use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
26use kernel::hil;
27use kernel::syscall::{CommandReturn, SyscallDriver};
28use kernel::utilities::cells::OptionalCell;
29use kernel::{ErrorCode, ProcessId};
30
31use capsules_core::driver;
33pub const DRIVER_NUM: usize = driver::NUM::NINEDOF as usize;
34
35#[derive(Clone, Copy, PartialEq)]
36pub enum NineDofCommand {
37 Exists,
38 ReadAccelerometer,
39 ReadMagnetometer,
40 ReadGyroscope,
41}
42
43pub struct App {
44 pending_command: bool,
45 command: NineDofCommand,
46 arg1: usize,
47}
48
49impl Default for App {
50 fn default() -> App {
51 App {
52 pending_command: false,
53 command: NineDofCommand::Exists,
54 arg1: 0,
55 }
56 }
57}
58
59pub struct NineDof<'a> {
60 drivers: &'a [&'a dyn hil::sensors::NineDof<'a>],
61 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
62 current_app: OptionalCell<ProcessId>,
63}
64
65impl<'a> NineDof<'a> {
66 pub fn new(
67 drivers: &'a [&'a dyn hil::sensors::NineDof<'a>],
68 grant: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
69 ) -> NineDof<'a> {
70 NineDof {
71 drivers,
72 apps: grant,
73 current_app: OptionalCell::empty(),
74 }
75 }
76
77 fn enqueue_command(
81 &self,
82 command: NineDofCommand,
83 arg1: usize,
84 processid: ProcessId,
85 ) -> CommandReturn {
86 self.apps
87 .enter(processid, |app, _| {
88 if self.current_app.is_none() {
89 self.current_app.set(processid);
90 let value = self.call_driver(command, arg1);
91 if value != Ok(()) {
92 self.current_app.clear();
93 }
94 CommandReturn::from(value)
95 } else {
96 if app.pending_command {
97 CommandReturn::failure(ErrorCode::BUSY)
98 } else {
99 app.pending_command = true;
100 app.command = command;
101 app.arg1 = arg1;
102 CommandReturn::success()
103 }
104 }
105 })
106 .unwrap_or_else(|err| {
107 let rcode: Result<(), ErrorCode> = err.into();
108 CommandReturn::from(rcode)
109 })
110 }
111
112 fn call_driver(&self, command: NineDofCommand, _: usize) -> Result<(), ErrorCode> {
113 match command {
114 NineDofCommand::ReadAccelerometer => {
115 let mut data = Err(ErrorCode::NODEVICE);
116 for driver in self.drivers.iter() {
117 data = driver.read_accelerometer();
118 if data == Ok(()) {
119 break;
120 }
121 }
122 data
123 }
124 NineDofCommand::ReadMagnetometer => {
125 let mut data = Err(ErrorCode::NODEVICE);
126 for driver in self.drivers.iter() {
127 data = driver.read_magnetometer();
128 if data == Ok(()) {
129 break;
130 }
131 }
132 data
133 }
134 NineDofCommand::ReadGyroscope => {
135 let mut data = Err(ErrorCode::NODEVICE);
136 for driver in self.drivers.iter() {
137 data = driver.read_gyroscope();
138 if data == Ok(()) {
139 break;
140 }
141 }
142 data
143 }
144 _ => Err(ErrorCode::NOSUPPORT),
145 }
146 }
147}
148
149impl hil::sensors::NineDofClient for NineDof<'_> {
150 fn callback(&self, arg1: usize, arg2: usize, arg3: usize) {
151 let mut finished_command = NineDofCommand::Exists;
155 let mut finished_command_arg = 0;
156 self.current_app.take().map(|processid| {
157 let _ = self.apps.enter(processid, |app, upcalls| {
158 app.pending_command = false;
159 finished_command = app.command;
160 finished_command_arg = app.arg1;
161 upcalls.schedule_upcall(0, (arg1, arg2, arg3)).ok();
162 });
163 });
164
165 for cntr in self.apps.iter() {
167 let processid = cntr.processid();
168 let started_command = cntr.enter(|app, upcalls| {
169 if app.pending_command
170 && app.command == finished_command
171 && app.arg1 == finished_command_arg
172 {
173 app.pending_command = false;
176 upcalls.schedule_upcall(0, (arg1, arg2, arg3)).ok();
177 false
178 } else if app.pending_command {
179 app.pending_command = false;
180 self.current_app.set(processid);
181 self.call_driver(app.command, app.arg1) == Ok(())
182 } else {
183 false
184 }
185 });
186 if started_command {
187 break;
188 }
189 }
190 }
191}
192
193impl SyscallDriver for NineDof<'_> {
194 fn command(
195 &self,
196 command_num: usize,
197 arg1: usize,
198 _: usize,
199 processid: ProcessId,
200 ) -> CommandReturn {
201 match command_num {
202 0 => CommandReturn::success(),
203 1 => self.enqueue_command(NineDofCommand::ReadAccelerometer, arg1, processid),
205
206 100 => self.enqueue_command(NineDofCommand::ReadMagnetometer, arg1, processid),
208
209 200 => self.enqueue_command(NineDofCommand::ReadGyroscope, arg1, processid),
211
212 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
213 }
214 }
215
216 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
217 self.apps.enter(processid, |_, _| {})
218 }
219}