1use core::cmp;
19
20use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
21use kernel::hil;
22use kernel::processbuffer::ReadableProcessBuffer;
23use kernel::syscall::{CommandReturn, SyscallDriver};
24use kernel::utilities::cells::{OptionalCell, TakeCell};
25use kernel::{ErrorCode, ProcessId};
26
27use capsules_core::driver;
29pub const DRIVER_NUM: usize = driver::NUM::TextScreen as usize;
30
31mod ro_allow {
33 pub const SHARED: usize = 0;
34 pub const COUNT: u8 = 1;
36}
37
38#[derive(Clone, Copy, PartialEq)]
39enum TextScreenCommand {
40 Idle,
41 GetResolution,
42 Display,
43 NoDisplay,
44 Blink,
45 NoBlink,
46 SetCursor,
47 NoCursor,
48 ShowCursor,
49 Write,
50 Clear,
51 Home,
52}
53
54pub struct App {
55 pending_command: bool,
56 write_len: usize,
57 command: TextScreenCommand,
58 data1: usize,
59 data2: usize,
60}
61
62impl Default for App {
63 fn default() -> App {
64 App {
65 pending_command: false,
66 write_len: 0,
67 command: TextScreenCommand::Idle,
68 data1: 1,
69 data2: 0,
70 }
71 }
72}
73
74pub struct TextScreen<'a> {
75 text_screen: &'a dyn hil::text_screen::TextScreen<'static>,
76 apps: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
77 current_app: OptionalCell<ProcessId>,
78 buffer: TakeCell<'static, [u8]>,
79}
80
81impl<'a> TextScreen<'a> {
82 pub fn new(
83 text_screen: &'static dyn hil::text_screen::TextScreen,
84 buffer: &'static mut [u8],
85 grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
86 ) -> TextScreen<'a> {
87 TextScreen {
88 text_screen,
89 apps: grant,
90 current_app: OptionalCell::empty(),
91 buffer: TakeCell::new(buffer),
92 }
93 }
94
95 fn enqueue_command(
96 &self,
97 command: TextScreenCommand,
98 data1: usize,
99 data2: usize,
100 processid: ProcessId,
101 ) -> CommandReturn {
102 let res = self
103 .apps
104 .enter(processid, |app, _| {
105 if self.current_app.is_none() {
106 self.current_app.set(processid);
107 app.data1 = data1;
108 app.data2 = data2;
109 app.command = command;
110 Ok(true)
111 } else {
112 if app.pending_command {
113 Err(ErrorCode::BUSY)
114 } else {
115 app.pending_command = true;
116 app.command = command;
117 app.data1 = data1;
118 app.data2 = data2;
119 Ok(false)
120 }
121 }
122 })
123 .map_err(ErrorCode::from);
124 let res = match res {
125 Ok(value) => value,
126 Err(err) => Err(err),
127 };
128 match res {
129 Ok(execute_now) => {
130 if execute_now {
131 match self.do_command() {
132 Ok(()) => CommandReturn::success(),
133 Err(err) => {
134 self.current_app.clear();
135 CommandReturn::failure(err)
136 }
137 }
138 } else {
139 CommandReturn::success()
140 }
141 }
142 Err(err) => CommandReturn::failure(err),
143 }
144 }
145
146 fn do_command(&self) -> Result<(), ErrorCode> {
147 let mut run_next = false;
148 let res = self.current_app.map_or(Err(ErrorCode::FAIL), |app| {
149 self.apps
150 .enter(app, |app, kernel_data| match app.command {
151 TextScreenCommand::GetResolution => {
152 let (x, y) = self.text_screen.get_size();
153 app.pending_command = false;
154 let _ = kernel_data
155 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Ok(())), x, y));
156 run_next = true;
157 Ok(())
158 }
159 TextScreenCommand::Display => self.text_screen.display_on(),
160 TextScreenCommand::NoDisplay => self.text_screen.display_off(),
161 TextScreenCommand::Blink => self.text_screen.blink_cursor_on(),
162 TextScreenCommand::NoBlink => self.text_screen.blink_cursor_off(),
163 TextScreenCommand::SetCursor => {
164 self.text_screen.set_cursor(app.data1, app.data2)
165 }
166 TextScreenCommand::NoCursor => self.text_screen.hide_cursor(),
167 TextScreenCommand::Write => {
168 if app.data1 > 0 {
169 app.write_len = app.data1;
170 let res = kernel_data
171 .get_readonly_processbuffer(ro_allow::SHARED)
172 .and_then(|shared| {
173 shared.enter(|to_write_buffer| {
174 self.buffer.take().map_or(Err(ErrorCode::BUSY), |buffer| {
175 let len = cmp::min(app.write_len, buffer.len());
176 for n in 0..len {
177 buffer[n] = to_write_buffer[n].get();
178 }
179 match self.text_screen.print(buffer, len) {
180 Ok(()) => Ok(()),
181 Err((ecode, buffer)) => {
182 self.buffer.replace(buffer);
183 Err(ecode)
184 }
185 }
186 })
187 })
188 });
189 match res {
190 Ok(Ok(())) => Ok(()),
191 Ok(Err(err)) => Err(err),
192 Err(err) => err.into(),
193 }
194 } else {
195 Err(ErrorCode::NOMEM)
196 }
197 }
198 TextScreenCommand::Clear => self.text_screen.clear(),
199 TextScreenCommand::Home => self.text_screen.clear(),
200 TextScreenCommand::ShowCursor => self.text_screen.show_cursor(),
201 _ => Err(ErrorCode::NOSUPPORT),
202 })
203 .map_err(ErrorCode::from)
204 });
205 if run_next {
206 self.run_next_command();
207 }
208 match res {
209 Ok(value) => value,
210 Err(err) => Err(err),
211 }
212 }
213
214 fn run_next_command(&self) {
215 for app in self.apps.iter() {
217 let processid = app.processid();
218 let current_command = app.enter(|app, _| {
219 if app.pending_command {
220 app.pending_command = false;
221 self.current_app.set(processid);
222 true
223 } else {
224 false
225 }
226 });
227 if current_command {
228 if self.do_command() != Ok(()) {
229 self.current_app.clear();
230 } else {
231 break;
232 }
233 }
234 }
235 }
236
237 fn schedule_callback(&self, data1: usize, data2: usize, data3: usize) {
238 self.current_app.take().map(|processid| {
239 let _ = self.apps.enter(processid, |app, kernel_data| {
240 app.pending_command = false;
241 kernel_data.schedule_upcall(0, (data1, data2, data3)).ok();
242 });
243 });
244 }
245}
246
247impl SyscallDriver for TextScreen<'_> {
248 fn command(
249 &self,
250 command_num: usize,
251 data1: usize,
252 data2: usize,
253 processid: ProcessId,
254 ) -> CommandReturn {
255 match command_num {
256 0 => CommandReturn::success(),
258 1 => self.enqueue_command(TextScreenCommand::GetResolution, data1, data2, processid),
260 2 => self.enqueue_command(TextScreenCommand::Display, data1, data2, processid),
262 3 => self.enqueue_command(TextScreenCommand::NoDisplay, data1, data2, processid),
264 4 => self.enqueue_command(TextScreenCommand::Blink, data1, data2, processid),
266 5 => self.enqueue_command(TextScreenCommand::NoBlink, data1, data2, processid),
268 6 => self.enqueue_command(TextScreenCommand::ShowCursor, data1, data2, processid),
270 7 => self.enqueue_command(TextScreenCommand::NoCursor, data1, data2, processid),
272 8 => self.enqueue_command(TextScreenCommand::Write, data1, data2, processid),
274 9 => self.enqueue_command(TextScreenCommand::Clear, data1, data2, processid),
276 10 => self.enqueue_command(TextScreenCommand::Home, data1, data2, processid),
278 11 => self.enqueue_command(TextScreenCommand::SetCursor, data1, data2, processid),
280 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
282 }
283 }
284
285 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
286 self.apps.enter(processid, |_, _| {})
287 }
288}
289
290impl hil::text_screen::TextScreenClient for TextScreen<'_> {
291 fn command_complete(&self, r: Result<(), ErrorCode>) {
292 self.schedule_callback(kernel::errorcode::into_statuscode(r), 0, 0);
293 self.run_next_command();
294 }
295
296 fn write_complete(&self, buffer: &'static mut [u8], len: usize, r: Result<(), ErrorCode>) {
297 self.buffer.replace(buffer);
298 self.schedule_callback(kernel::errorcode::into_statuscode(r), len, 0);
299 self.run_next_command();
300 }
301}