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