capsules_extra/
screen.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//! Provides userspace with access to the screen.
6//!
7//! Usage
8//! -----
9//!
10//! You need a screen that provides the `hil::screen::Screen` trait.
11//!
12//! ```rust,ignore
13//! let screen =
14//!     components::screen::ScreenComponent::new(board_kernel, tft).finalize();
15//! ```
16
17use core::cell::Cell;
18
19use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
20use kernel::hil;
21use kernel::hil::screen::{ScreenPixelFormat, ScreenRotation};
22use kernel::processbuffer::ReadableProcessBuffer;
23use kernel::syscall::{CommandReturn, SyscallDriver};
24use kernel::utilities::cells::{OptionalCell, TakeCell};
25use kernel::utilities::leasable_buffer::SubSliceMut;
26use kernel::{ErrorCode, ProcessId};
27
28/// Syscall driver number.
29use capsules_core::driver;
30pub const DRIVER_NUM: usize = driver::NUM::Screen as usize;
31
32/// Ids for read-only allow buffers
33mod ro_allow {
34    pub const SHARED: usize = 0;
35    /// The number of allow buffers the kernel stores for this grant
36    pub const COUNT: u8 = 1;
37}
38
39fn screen_rotation_from(screen_rotation: usize) -> Option<ScreenRotation> {
40    match screen_rotation {
41        0 => Some(ScreenRotation::Normal),
42        1 => Some(ScreenRotation::Rotated90),
43        2 => Some(ScreenRotation::Rotated180),
44        3 => Some(ScreenRotation::Rotated270),
45        _ => None,
46    }
47}
48
49fn screen_pixel_format_from(screen_pixel_format: usize) -> Option<ScreenPixelFormat> {
50    match screen_pixel_format {
51        0 => Some(ScreenPixelFormat::Mono),
52        1 => Some(ScreenPixelFormat::RGB_332),
53        2 => Some(ScreenPixelFormat::RGB_565),
54        3 => Some(ScreenPixelFormat::RGB_888),
55        4 => Some(ScreenPixelFormat::ARGB_8888),
56        _ => None,
57    }
58}
59
60#[derive(Clone, Copy, PartialEq)]
61enum ScreenCommand {
62    Nop,
63    SetBrightness(u16),
64    SetPower(bool),
65    SetInvert(bool),
66    SetRotation(ScreenRotation),
67    SetResolution {
68        width: usize,
69        height: usize,
70    },
71    SetPixelFormat(ScreenPixelFormat),
72    SetWriteFrame {
73        x: usize,
74        y: usize,
75        width: usize,
76        height: usize,
77    },
78    Write(usize),
79    Fill,
80}
81
82fn pixels_in_bytes(pixels: usize, bits_per_pixel: usize) -> usize {
83    let bytes = pixels * bits_per_pixel / 8;
84    if pixels * bits_per_pixel % 8 != 0 {
85        bytes + 1
86    } else {
87        bytes
88    }
89}
90
91pub struct App {
92    pending_command: bool,
93    write_position: usize,
94    write_len: usize,
95    command: ScreenCommand,
96    width: usize,
97    height: usize,
98}
99
100impl Default for App {
101    fn default() -> App {
102        App {
103            pending_command: false,
104            command: ScreenCommand::Nop,
105            width: 0,
106            height: 0,
107            write_len: 0,
108            write_position: 0,
109        }
110    }
111}
112
113pub struct Screen<'a> {
114    screen: &'a dyn hil::screen::Screen<'a>,
115    screen_setup: Option<&'a dyn hil::screen::ScreenSetup<'a>>,
116    apps: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
117    current_process: OptionalCell<ProcessId>,
118    pixel_format: Cell<ScreenPixelFormat>,
119    buffer: TakeCell<'static, [u8]>,
120}
121
122impl<'a> Screen<'a> {
123    pub fn new(
124        screen: &'a dyn hil::screen::Screen<'a>,
125        screen_setup: Option<&'a dyn hil::screen::ScreenSetup<'a>>,
126        buffer: &'static mut [u8],
127        grant: Grant<App, UpcallCount<1>, AllowRoCount<{ ro_allow::COUNT }>, AllowRwCount<0>>,
128    ) -> Screen<'a> {
129        Screen {
130            screen,
131            screen_setup,
132            apps: grant,
133            current_process: OptionalCell::empty(),
134            pixel_format: Cell::new(screen.get_pixel_format()),
135            buffer: TakeCell::new(buffer),
136        }
137    }
138
139    // Check to see if we are doing something. If not,
140    // go ahead and do this command. If so, this is queued
141    // and will be run when the pending command completes.
142    fn enqueue_command(&self, command: ScreenCommand, process_id: ProcessId) -> CommandReturn {
143        match self
144            .apps
145            .enter(process_id, |app, _| {
146                if app.pending_command {
147                    CommandReturn::failure(ErrorCode::BUSY)
148                } else {
149                    app.pending_command = true;
150                    app.command = command;
151                    app.write_position = 0;
152                    CommandReturn::success()
153                }
154            })
155            .map_err(ErrorCode::from)
156        {
157            Err(e) => CommandReturn::failure(e),
158            Ok(r) => {
159                if self.current_process.is_none() {
160                    self.current_process.set(process_id);
161                    let r = self.call_screen(command, process_id);
162                    if r != Ok(()) {
163                        self.current_process.clear();
164                    }
165                    CommandReturn::from(r)
166                } else {
167                    r
168                }
169            }
170        }
171    }
172
173    fn is_len_multiple_color_depth(&self, len: usize) -> bool {
174        let depth = pixels_in_bytes(1, self.screen.get_pixel_format().get_bits_per_pixel());
175        (len % depth) == 0
176    }
177
178    fn call_screen(&self, command: ScreenCommand, process_id: ProcessId) -> Result<(), ErrorCode> {
179        match command {
180            ScreenCommand::SetBrightness(brighness) => self.screen.set_brightness(brighness),
181            ScreenCommand::SetPower(enabled) => self.screen.set_power(enabled),
182            ScreenCommand::SetInvert(enabled) => self.screen.set_invert(enabled),
183            ScreenCommand::SetRotation(rotation) => {
184                if let Some(screen) = self.screen_setup {
185                    screen.set_rotation(rotation)
186                } else {
187                    Err(ErrorCode::NOSUPPORT)
188                }
189            }
190            ScreenCommand::SetResolution { width, height } => {
191                if let Some(screen) = self.screen_setup {
192                    screen.set_resolution((width, height))
193                } else {
194                    Err(ErrorCode::NOSUPPORT)
195                }
196            }
197            ScreenCommand::SetPixelFormat(pixel_format) => {
198                if let Some(screen) = self.screen_setup {
199                    screen.set_pixel_format(pixel_format)
200                } else {
201                    Err(ErrorCode::NOSUPPORT)
202                }
203            }
204            ScreenCommand::Fill => {
205                match self
206                    .apps
207                    .enter(process_id, |app, kernel_data| {
208                        let len = kernel_data
209                            .get_readonly_processbuffer(ro_allow::SHARED)
210                            .map_or(0, |shared| shared.len());
211                        // Ensure we have a buffer that is the correct size
212                        if len == 0 {
213                            Err(ErrorCode::NOMEM)
214                        } else if !self.is_len_multiple_color_depth(len) {
215                            Err(ErrorCode::INVAL)
216                        } else {
217                            app.write_position = 0;
218                            app.write_len = pixels_in_bytes(
219                                app.width * app.height,
220                                self.pixel_format.get().get_bits_per_pixel(),
221                            );
222                            Ok(())
223                        }
224                    })
225                    .unwrap_or_else(|err| err.into())
226                {
227                    Err(e) => Err(e),
228                    Ok(()) => self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
229                        let len = self.fill_next_buffer_for_write(buffer);
230                        if len > 0 {
231                            let mut data = SubSliceMut::new(buffer);
232                            data.slice(..len);
233                            self.screen.write(data, false)
234                        } else {
235                            self.buffer.replace(buffer);
236                            self.run_next_command(kernel::errorcode::into_statuscode(Ok(())), 0, 0);
237                            Ok(())
238                        }
239                    }),
240                }
241            }
242
243            ScreenCommand::Write(data_len) => {
244                match self
245                    .apps
246                    .enter(process_id, |app, kernel_data| {
247                        let len = kernel_data
248                            .get_readonly_processbuffer(ro_allow::SHARED)
249                            .map_or(0, |shared| shared.len())
250                            .min(data_len);
251                        // Ensure we have a buffer that is the correct size
252                        if len == 0 {
253                            Err(ErrorCode::NOMEM)
254                        } else if !self.is_len_multiple_color_depth(len) {
255                            Err(ErrorCode::INVAL)
256                        } else {
257                            app.write_position = 0;
258                            app.write_len = len;
259                            Ok(())
260                        }
261                    })
262                    .unwrap_or_else(|err| err.into())
263                {
264                    Ok(()) => self.buffer.take().map_or(Err(ErrorCode::FAIL), |buffer| {
265                        let len = self.fill_next_buffer_for_write(buffer);
266                        if len > 0 {
267                            let mut data = SubSliceMut::new(buffer);
268                            data.slice(..len);
269                            self.screen.write(data, false)
270                        } else {
271                            self.buffer.replace(buffer);
272                            self.run_next_command(kernel::errorcode::into_statuscode(Ok(())), 0, 0);
273                            Ok(())
274                        }
275                    }),
276                    Err(e) => Err(e),
277                }
278            }
279            ScreenCommand::SetWriteFrame {
280                x,
281                y,
282                width,
283                height,
284            } => self
285                .apps
286                .enter(process_id, |app, _| {
287                    app.write_position = 0;
288                    app.width = width;
289                    app.height = height;
290
291                    self.screen.set_write_frame(x, y, width, height)
292                })
293                .unwrap_or_else(|err| err.into()),
294            _ => Err(ErrorCode::NOSUPPORT),
295        }
296    }
297
298    fn schedule_callback(&self, data1: usize, data2: usize, data3: usize) {
299        self.current_process.take().map(|process_id| {
300            let _ = self.apps.enter(process_id, |app, upcalls| {
301                app.pending_command = false;
302                upcalls.schedule_upcall(0, (data1, data2, data3)).ok();
303            });
304        });
305    }
306
307    fn run_next_command(&self, data1: usize, data2: usize, data3: usize) {
308        self.schedule_callback(data1, data2, data3);
309
310        let mut command = ScreenCommand::Nop;
311
312        // Check if there are any pending events.
313        for app in self.apps.iter() {
314            let process_id = app.processid();
315            let start_command = app.enter(|app, _| {
316                if app.pending_command {
317                    app.pending_command = false;
318                    command = app.command;
319                    self.current_process.set(process_id);
320                    true
321                } else {
322                    false
323                }
324            });
325            if start_command {
326                match self.call_screen(command, process_id) {
327                    Err(err) => {
328                        self.current_process.clear();
329                        self.schedule_callback(kernel::errorcode::into_statuscode(Err(err)), 0, 0);
330                    }
331                    Ok(()) => {
332                        break;
333                    }
334                }
335            }
336        }
337    }
338
339    fn fill_next_buffer_for_write(&self, buffer: &mut [u8]) -> usize {
340        self.current_process.map_or(0, |process_id| {
341            self.apps
342                .enter(process_id, |app, kernel_data| {
343                    let position = app.write_position;
344                    let mut len = app.write_len;
345                    if position < len {
346                        let buffer_size = buffer.len();
347                        let chunk_number = position / buffer_size;
348                        let initial_pos = chunk_number * buffer_size;
349                        let mut pos = initial_pos;
350                        match app.command {
351                            ScreenCommand::Write(_) => {
352                                let res = kernel_data
353                                    .get_readonly_processbuffer(ro_allow::SHARED)
354                                    .and_then(|shared| {
355                                        shared.enter(|s| {
356                                            let mut count = 0;
357                                            let mut chunks = s.chunks(buffer_size);
358                                            if let Some(chunk) = chunks.nth(chunk_number) {
359                                                for (i, byte) in chunk.iter().enumerate() {
360                                                    if pos < len {
361                                                        buffer[i] = byte.get();
362                                                        count += 1;
363                                                        pos += 1;
364                                                    } else {
365                                                        break;
366                                                    }
367                                                }
368                                                count
369                                            } else {
370                                                // stop writing
371                                                0
372                                            }
373                                        })
374                                    })
375                                    .unwrap_or(0);
376                                if res > 0 {
377                                    app.write_position = pos;
378                                }
379                                res
380                            }
381                            ScreenCommand::Fill => {
382                                // TODO bytes per pixel
383                                len -= position;
384                                let bytes_per_pixel = pixels_in_bytes(
385                                    1,
386                                    self.pixel_format.get().get_bits_per_pixel(),
387                                );
388                                let mut write_len = buffer_size / bytes_per_pixel;
389                                if write_len > len {
390                                    write_len = len
391                                }
392                                app.write_position += write_len * bytes_per_pixel;
393                                kernel_data
394                                    .get_readonly_processbuffer(ro_allow::SHARED)
395                                    .and_then(|shared| {
396                                        shared.enter(|data| {
397                                            let mut bytes = data.iter();
398                                            // bytes per pixel
399                                            for i in 0..bytes_per_pixel {
400                                                if let Some(byte) = bytes.next() {
401                                                    buffer[i] = byte.get();
402                                                }
403                                            }
404                                            for i in 1..write_len {
405                                                // bytes per pixel
406                                                for j in 0..bytes_per_pixel {
407                                                    buffer[bytes_per_pixel * i + j] = buffer[j]
408                                                }
409                                            }
410                                            write_len * bytes_per_pixel
411                                        })
412                                    })
413                                    .unwrap_or(0)
414                            }
415                            _ => 0,
416                        }
417                    } else {
418                        0
419                    }
420                })
421                .unwrap_or(0)
422        })
423    }
424}
425
426impl hil::screen::ScreenClient for Screen<'_> {
427    fn command_complete(&self, r: Result<(), ErrorCode>) {
428        self.run_next_command(kernel::errorcode::into_statuscode(r), 0, 0);
429    }
430
431    fn write_complete(&self, data: SubSliceMut<'static, u8>, r: Result<(), ErrorCode>) {
432        let buffer = data.take();
433        let len = self.fill_next_buffer_for_write(buffer);
434
435        if r == Ok(()) && len > 0 {
436            let mut data = SubSliceMut::new(buffer);
437            data.slice(..len);
438            let _ = self.screen.write(data, true);
439        } else {
440            self.buffer.replace(buffer);
441            self.run_next_command(kernel::errorcode::into_statuscode(r), 0, 0);
442        }
443    }
444
445    fn screen_is_ready(&self) {
446        self.run_next_command(kernel::errorcode::into_statuscode(Ok(())), 0, 0);
447    }
448}
449
450impl hil::screen::ScreenSetupClient for Screen<'_> {
451    fn command_complete(&self, r: Result<(), ErrorCode>) {
452        self.run_next_command(kernel::errorcode::into_statuscode(r), 0, 0);
453    }
454}
455
456impl SyscallDriver for Screen<'_> {
457    fn command(
458        &self,
459        command_num: usize,
460        data1: usize,
461        data2: usize,
462        process_id: ProcessId,
463    ) -> CommandReturn {
464        match command_num {
465            // Driver existence check
466            0 => CommandReturn::success(),
467            // Does it have the screen setup
468            1 => CommandReturn::success_u32(self.screen_setup.is_some() as u32),
469            // Set power
470            2 => self.enqueue_command(ScreenCommand::SetPower(data1 != 0), process_id),
471            // Set Brightness
472            3 => self.enqueue_command(ScreenCommand::SetBrightness(data1 as u16), process_id),
473            // Invert on (deprecated)
474            4 => self.enqueue_command(ScreenCommand::SetInvert(true), process_id),
475            // Invert off (deprecated)
476            5 => self.enqueue_command(ScreenCommand::SetInvert(false), process_id),
477            // Set Invert
478            6 => self.enqueue_command(ScreenCommand::SetInvert(data1 != 0), process_id),
479
480            // Get Resolution Modes count
481            11 => {
482                if let Some(screen) = self.screen_setup {
483                    CommandReturn::success_u32(screen.get_num_supported_resolutions() as u32)
484                } else {
485                    CommandReturn::failure(ErrorCode::NOSUPPORT)
486                }
487            }
488            // Get Resolution Mode Width and Height
489            12 => {
490                if let Some(screen) = self.screen_setup {
491                    match screen.get_supported_resolution(data1) {
492                        Some((width, height)) if width > 0 && height > 0 => {
493                            CommandReturn::success_u32_u32(width as u32, height as u32)
494                        }
495                        _ => CommandReturn::failure(ErrorCode::INVAL),
496                    }
497                } else {
498                    CommandReturn::failure(ErrorCode::NOSUPPORT)
499                }
500            }
501
502            // Get pixel format Modes count
503            13 => {
504                if let Some(screen) = self.screen_setup {
505                    CommandReturn::success_u32(screen.get_num_supported_pixel_formats() as u32)
506                } else {
507                    CommandReturn::failure(ErrorCode::NOSUPPORT)
508                }
509            }
510            // Get supported pixel format
511            14 => {
512                if let Some(screen) = self.screen_setup {
513                    match screen.get_supported_pixel_format(data1) {
514                        Some(pixel_format) => CommandReturn::success_u32(pixel_format as u32),
515                        _ => CommandReturn::failure(ErrorCode::INVAL),
516                    }
517                } else {
518                    CommandReturn::failure(ErrorCode::NOSUPPORT)
519                }
520            }
521
522            // Get Rotation
523            21 => CommandReturn::success_u32(self.screen.get_rotation() as u32),
524            // Set Rotation
525            22 => self.enqueue_command(
526                ScreenCommand::SetRotation(
527                    screen_rotation_from(data1).unwrap_or(ScreenRotation::Normal),
528                ),
529                process_id,
530            ),
531
532            // Get Resolution
533            23 => {
534                let (width, height) = self.screen.get_resolution();
535                CommandReturn::success_u32_u32(width as u32, height as u32)
536            }
537            // Set Resolution
538            24 => self.enqueue_command(
539                ScreenCommand::SetResolution {
540                    width: data1,
541                    height: data2,
542                },
543                process_id,
544            ),
545
546            // Get pixel format
547            25 => CommandReturn::success_u32(self.screen.get_pixel_format() as u32),
548            // Set pixel format
549            26 => {
550                if let Some(pixel_format) = screen_pixel_format_from(data1) {
551                    self.enqueue_command(ScreenCommand::SetPixelFormat(pixel_format), process_id)
552                } else {
553                    CommandReturn::failure(ErrorCode::INVAL)
554                }
555            }
556
557            // Set Write Frame
558            100 => self.enqueue_command(
559                ScreenCommand::SetWriteFrame {
560                    x: (data1 >> 16) & 0xFFFF,
561                    y: data1 & 0xFFFF,
562                    width: (data2 >> 16) & 0xFFFF,
563                    height: data2 & 0xFFFF,
564                },
565                process_id,
566            ),
567            // Write
568            200 => self.enqueue_command(ScreenCommand::Write(data1), process_id),
569            // Fill
570            300 => self.enqueue_command(ScreenCommand::Fill, process_id),
571
572            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
573        }
574    }
575
576    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
577        self.apps.enter(processid, |_, _| {})
578    }
579}