1use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
30use kernel::hil;
31use kernel::processbuffer::ReadableProcessBuffer;
32use kernel::syscall::{CommandReturn, SyscallDriver};
33use kernel::utilities::cells::{OptionalCell, TakeCell};
34use kernel::utilities::leasable_buffer::SubSliceMut;
35use kernel::{ErrorCode, ProcessId};
36
37use capsules_core::driver;
39pub const DRIVER_NUM: usize = driver::NUM::Screen as usize;
40
41mod ro_allow {
43 pub const SHARED: usize = 0;
44 pub const COUNT: u8 = 1;
46}
47
48#[derive(Clone, Copy, PartialEq)]
49enum ScreenCommand {
50 WriteSetFrame,
51 WriteBuffer,
52}
53
54fn pixels_in_bytes(pixels: usize, bits_per_pixel: usize) -> usize {
55 let bytes = pixels * bits_per_pixel / 8;
56 if pixels * bits_per_pixel % 8 != 0 {
57 bytes + 1
58 } else {
59 bytes
60 }
61}
62
63#[derive(Default, Clone, Copy, PartialEq)]
65pub struct Frame {
66 x: usize,
68 y: usize,
70 width: usize,
72 height: usize,
74}
75
76pub struct AppScreenRegion {
77 app_id: kernel::process::ShortId,
78 frame: Frame,
79}
80
81impl AppScreenRegion {
82 pub fn new(
83 app_id: kernel::process::ShortId,
84 x: usize,
85 y: usize,
86 width: usize,
87 height: usize,
88 ) -> Self {
89 Self {
90 app_id,
91 frame: Frame {
92 x,
93 y,
94 width,
95 height,
96 },
97 }
98 }
99}
100
101#[derive(Default)]
102pub struct App {
103 command: Option<ScreenCommand>,
105 frame: Frame,
107}
108
109pub struct ScreenShared<'a, S: hil::screen::Screen<'a>> {
113 screen: &'a S,
115
116 apps: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
118
119 apps_regions: &'a [AppScreenRegion],
121
122 current_process: OptionalCell<ProcessId>,
124
125 buffer: TakeCell<'static, [u8]>,
127}
128
129impl<'a, S: hil::screen::Screen<'a>> ScreenShared<'a, S> {
130 pub fn new(
131 screen: &'a S,
132 grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
133 buffer: &'static mut [u8],
134 apps_regions: &'a [AppScreenRegion],
135 ) -> ScreenShared<'a, S> {
136 ScreenShared {
137 screen,
138 apps: grant,
139 current_process: OptionalCell::empty(),
140 buffer: TakeCell::new(buffer),
141 apps_regions,
142 }
143 }
144
145 fn enqueue_command(&self, command: ScreenCommand, process_id: ProcessId) -> CommandReturn {
147 let ret = self
148 .apps
149 .enter(process_id, |app, _| {
150 if app.command.is_some() {
151 Err(ErrorCode::BUSY)
152 } else {
153 app.command = Some(command);
154 Ok(())
155 }
156 })
157 .map_err(ErrorCode::from)
158 .and_then(|r| r)
159 .into();
160
161 if self.current_process.is_none() {
162 self.run_next_command();
163 }
164
165 ret
166 }
167
168 fn calculate_absolute_frame(&self, app_screen_region_frame: Frame, app_frame: Frame) -> Frame {
172 let mut absolute_x = app_screen_region_frame.x + app_frame.x;
174 let mut absolute_y = app_screen_region_frame.y + app_frame.y;
175 let mut absolute_w = app_frame.width;
177 let mut absolute_h = app_frame.height;
178
179 absolute_x = core::cmp::min(
181 app_screen_region_frame.x + app_screen_region_frame.width,
182 absolute_x,
183 );
184 absolute_y = core::cmp::min(
185 app_screen_region_frame.y + app_screen_region_frame.height,
186 absolute_y,
187 );
188 absolute_w = core::cmp::min(
189 app_screen_region_frame.x + app_screen_region_frame.width - absolute_x,
190 absolute_w,
191 );
192 absolute_h = core::cmp::min(
193 app_screen_region_frame.y + app_screen_region_frame.height - absolute_y,
194 absolute_h,
195 );
196
197 Frame {
198 x: absolute_x,
199 y: absolute_y,
200 width: absolute_w,
201 height: absolute_h,
202 }
203 }
204
205 fn call_screen(
206 &self,
207 process_id: ProcessId,
208 app_screen_region_frame: Frame,
209 ) -> Result<(), ErrorCode> {
210 self.apps
211 .enter(process_id, |app, kernel_data| {
212 match app.command {
213 Some(ScreenCommand::WriteSetFrame) => {
214 let absolute_frame =
215 self.calculate_absolute_frame(app_screen_region_frame, app.frame);
216
217 app.command = Some(ScreenCommand::WriteBuffer);
218 self.screen
219 .set_write_frame(
220 absolute_frame.x,
221 absolute_frame.y,
222 absolute_frame.width,
223 absolute_frame.height,
224 )
225 .inspect_err(|_| {
226 app.command = None;
227 })
228 }
229 Some(ScreenCommand::WriteBuffer) => {
230 app.command = None;
231 kernel_data
232 .get_readonly_processbuffer(ro_allow::SHARED)
233 .map(|allow_buf| {
234 let len = allow_buf.len();
235
236 if len == 0 {
237 Err(ErrorCode::NOMEM)
238 } else if !self.is_len_multiple_color_depth(len) {
239 Err(ErrorCode::INVAL)
240 } else {
241 self.buffer.take().map_or(Err(ErrorCode::FAIL), |buffer| {
244 let copy_len =
245 core::cmp::min(buffer.len(), allow_buf.len());
246 allow_buf.enter(|ab| {
247 ab[..copy_len].copy_to_slice(&mut buffer[..copy_len])
249 })?;
250
251 let mut data = SubSliceMut::new(buffer);
253 data.slice(..copy_len);
254 self.screen.write(data, false)
255 })
256 }
257 })
258 .map_err(ErrorCode::from)
259 .and_then(|r| r)
260 }
261 _ => Err(ErrorCode::NOSUPPORT),
262 }
263 })
264 .map_err(ErrorCode::from)
265 .and_then(|r| r)
266 }
267
268 fn schedule_callback(&self, process_id: ProcessId, data1: usize, data2: usize, data3: usize) {
269 let _ = self.apps.enter(process_id, |_app, kernel_data| {
270 kernel_data.schedule_upcall(0, (data1, data2, data3)).ok();
271 });
272 }
273
274 fn get_app_screen_region_frame(&self, process_id: ProcessId) -> Option<Frame> {
275 let short_id = process_id.short_app_id();
276
277 for app_screen_region in self.apps_regions {
278 if short_id == app_screen_region.app_id {
279 return Some(app_screen_region.frame);
280 }
281 }
282 None
283 }
284
285 fn run_next_command(&self) {
286 let ran_cmd = self.current_process.map_or(false, |process_id| {
287 let app_region_frame = self.get_app_screen_region_frame(process_id);
288
289 app_region_frame.is_some_and(|frame| {
290 let r = self.call_screen(process_id, frame);
291 if r.is_err() {
292 self.current_process.take().map(|process_id| {
295 self.schedule_callback(
296 process_id,
297 kernel::errorcode::into_statuscode(r),
298 0,
299 0,
300 );
301 });
302 false
303 } else {
304 true
305 }
306 })
307 });
308
309 if !ran_cmd {
310 for app in self.apps.iter() {
312 let process_id = app.processid();
313
314 let frame_maybe = app.enter(|app, _| {
317 if app.command.is_some() {
318 self.get_app_screen_region_frame(process_id)
319 } else {
320 None
321 }
322 });
323
324 if frame_maybe.is_some() {
326 match frame_maybe {
327 Some(frame) => {
328 self.current_process.set(process_id);
331 match self.call_screen(process_id, frame) {
332 Ok(()) => {
333 break;
336 }
337 Err(err) => {
338 self.current_process.clear();
342 self.schedule_callback(
343 process_id,
344 kernel::errorcode::into_statuscode(Err(err)),
345 0,
346 0,
347 );
348 }
349 }
350 }
351 None => {}
352 }
353 }
354 }
355 }
356 }
357
358 fn is_len_multiple_color_depth(&self, len: usize) -> bool {
359 let depth = pixels_in_bytes(1, self.screen.get_pixel_format().get_bits_per_pixel());
360 (len % depth) == 0
361 }
362}
363
364impl<'a, S: hil::screen::Screen<'a>> hil::screen::ScreenClient for ScreenShared<'a, S> {
365 fn command_complete(&self, r: Result<(), ErrorCode>) {
366 if r.is_err() {
367 self.current_process.take().map(|process_id| {
368 self.schedule_callback(process_id, kernel::errorcode::into_statuscode(r), 0, 0);
369 });
370 }
371
372 self.run_next_command();
373 }
374
375 fn write_complete(&self, data: SubSliceMut<'static, u8>, r: Result<(), ErrorCode>) {
376 self.buffer.replace(data.take());
377
378 self.current_process.take().map(|process_id| {
380 self.schedule_callback(process_id, kernel::errorcode::into_statuscode(r), 0, 0);
381 });
382
383 self.run_next_command();
384 }
385
386 fn screen_is_ready(&self) {
387 self.run_next_command();
388 }
389}
390
391impl<'a, S: hil::screen::Screen<'a>> SyscallDriver for ScreenShared<'a, S> {
392 fn command(
393 &self,
394 command_num: usize,
395 data1: usize,
396 data2: usize,
397 process_id: ProcessId,
398 ) -> CommandReturn {
399 match command_num {
400 0 => CommandReturn::success(),
402
403 21 => CommandReturn::success_u32(self.screen.get_rotation() as u32),
405
406 23 => match self.get_app_screen_region_frame(process_id) {
408 Some(frame) => {
409 CommandReturn::success_u32_u32(frame.width as u32, frame.height as u32)
410 }
411 None => CommandReturn::failure(ErrorCode::NOSUPPORT),
412 },
413
414 25 => CommandReturn::success_u32(self.screen.get_pixel_format() as u32),
416
417 100 => {
419 let frame = Frame {
420 x: (data1 >> 16) & 0xFFFF,
421 y: data1 & 0xFFFF,
422 width: (data2 >> 16) & 0xFFFF,
423 height: data2 & 0xFFFF,
424 };
425
426 self.apps
427 .enter(process_id, |app, kernel_data| {
428 app.frame = frame;
429
430 let _ = kernel_data
432 .schedule_upcall(0, (kernel::errorcode::into_statuscode(Ok(())), 0, 0));
433 })
434 .map_err(ErrorCode::from)
435 .into()
436 }
437
438 200 => {
440 if self.get_app_screen_region_frame(process_id).is_none() {
443 CommandReturn::failure(ErrorCode::NOSUPPORT)
444 } else {
445 self.enqueue_command(ScreenCommand::WriteSetFrame, process_id)
446 }
447 }
448
449 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
450 }
451 }
452
453 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
454 self.apps.enter(processid, |_, _| {})
455 }
456}