Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ libtock_low_level_debug = { path = "apis/kernel/low_level_debug" }
libtock_ninedof = { path = "apis/sensors/ninedof" }
libtock_platform = { path = "platform" }
libtock_proximity = { path = "apis/sensors/proximity" }
libtock_display = { path = "apis/display/screen" }
libtock_rng = { path = "apis/peripherals/rng" }
libtock_runtime = { path = "runtime" }
libtock_small_panic = { path = "panic_handlers/small_panic" }
Expand Down Expand Up @@ -82,6 +83,7 @@ members = [
"apis/sensors/air_quality",
"apis/sensors/ambient_light",
"apis/sensors/ninedof",
"apis/display/screen",
"apis/sensors/proximity",
"apis/sensors/temperature",
"apis/storage/key_value",
Expand Down
15 changes: 15 additions & 0 deletions apis/display/screen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "libtock_display"
version = "0.1.0"
authors = ["Tock Project Developers <[email protected]>"]
license = "Apache-2.0 OR MIT"
edition = "2021"
repository = "https://www.github.com/tock/libtock-rs"
rust-version.workspace = true
description = "libtock display driver"

[dependencies]
libtock_platform = { path = "../../../platform" }

[dev-dependencies]
libtock_unittest = { path = "../../../unittest" }
318 changes: 318 additions & 0 deletions apis/display/screen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
#![no_std]
use core::cell::Cell;
use libtock_platform::allow_ro::AllowRo;
use libtock_platform::share;
use libtock_platform::subscribe::Subscribe;
use libtock_platform::{self as platform, DefaultConfig};
use libtock_platform::{ErrorCode, Syscalls};

pub struct Screen<S: Syscalls, C: Config = DefaultConfig>(S, C);

impl<S: Syscalls, C: Config> Screen<S, C> {
/// Check if the Screen driver exists
pub fn exists() -> Result<(), ErrorCode> {
let val = S::command(DRIVER_NUM, command::EXISTS, 0, 0).is_success();
if val {
Ok(())
} else {
Err(ErrorCode::Fail)
}
}

/// Perform initial screen setup
pub fn screen_setup() -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::SCREEN_SETUP, 0, 0).to_result()
}

/// Turn on Screen power
pub fn set_power(value: usize) -> Result<(), ErrorCode> {
if value != 0 {
S::command(DRIVER_NUM, command::SET_POWER, value as u32, 0).to_result()
} else {
Err(ErrorCode::Invalid)
}
}

/// Set screen brightness, wait for completion via subscribe
pub fn set_brightness(value: usize) -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::SET_BRIGHTNESS, value as u32, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Turn on screen color inversion
pub fn set_invert_on() -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::SET_INVERT_ON, 0, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Turn off screen color inversion
pub fn set_invert_off() -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::SET_INVERT_OFF, 0, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Set inversion using a numeric value (non-zero = on)
pub fn set_invert(value: usize) -> Result<(), ErrorCode> {
if value != 0 {
S::command(DRIVER_NUM, command::SET_INVERT, value as u32, 0).to_result()
} else {
Err(ErrorCode::Invalid)
}
}

/// Get the number of supported resolution modes
pub fn get_resolution_modes_count() -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::GET_RESOLUTION_MODES_COUNT, 0, 0).to_result()
}

/// Get width and height for a given resolution index
pub fn get_resolution_width_height(index: usize) -> Result<(u32, u32), ErrorCode> {
S::command(
DRIVER_NUM,
command::GET_RESOLUTION_WIDTH_HEIGHT,
index as u32,
0,
)
.to_result()
}

/// Get the number of supported pixel modes
pub fn pixel_modes_count() -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::PIXEL_MODES_COUNT, 0, 0).to_result()
}

/// Get the pixel format at a specific index
pub fn pixel_format(index: usize) -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::PIXEL_FORMAT, index as u32, 0).to_result()
}

/// Get the current rotation of the screen
pub fn get_rotation() -> Result<u32, ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::GET_ROTATION, 0, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Set the screen rotation
pub fn set_rotation(rotation: usize) -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::SET_ROTATION, rotation as u32, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Get the currently set screen resolution
pub fn get_resolution() -> Result<(u32, u32), ErrorCode> {
S::command(DRIVER_NUM, command::GET_RESOLUTION, 0, 0).to_result()
}

/// Set the screen resolution
pub fn set_resolution(width: usize, height: usize) -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(
DRIVER_NUM,
command::SET_RESOLUTION,
width as u32,
height as u32,
)
.to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Get the currently set pixel format
pub fn get_pixel_format() -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::GET_PIXEL_FORMAT, 0, 0).to_result()
}

/// Set the pixel format
pub fn set_pixel_format(format: usize) -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val =
S::command(DRIVER_NUM, command::SET_PIXEL_FORMAT, format as u32, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Define the region of the screen that will be written to
pub fn set_write_frame(x: u32, y: u32, width: u32, height: u32) -> Result<(), ErrorCode> {
let data1: u32 = ((x & 0xFFFF) << 16_u8) | (y & 0xFFFF);
let data2: u32 = ((width & 0xFFFF) << 16_u8) | (height & 0xFFFF);
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::SET_WRITE_FRAME, data1, data2).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Write data to the screen using the given buffer
pub fn write(s: &[u8]) -> Result<(), ErrorCode> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope::<
(
AllowRo<_, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>,
Subscribe<_, DRIVER_NUM, { subscribe::WRITE }>,
),
_,
_,
>(|handle| {
let (allow_ro, subscribe) = handle.split();
S::allow_ro::<C, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>(allow_ro, s)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::WRITE, s.len() as u32, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
}

/// Fill the screen
pub fn fill(s: &mut [u8], color: u16) -> Result<(), ErrorCode> {
if s.len() >= 2 {
s[0] = ((color >> 8) & 0xFF) as u8;
s[1] = (color & 0xFF) as u8;

let called: Cell<Option<(u32,)>> = Cell::new(None);
share::scope::<
(
AllowRo<_, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>,
Subscribe<_, DRIVER_NUM, { subscribe::WRITE }>,
),
_,
_,
>(|handle| {
let (allow_ro, subscribe) = handle.split();
S::allow_ro::<C, DRIVER_NUM, { allow_ro::WRITE_BUFFER_ID }>(allow_ro, s)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::WRITE }>(subscribe, &called)?;
let val = S::command(DRIVER_NUM, command::FILL, 0, 0).to_result();
loop {
S::yield_wait();
if let Some((_,)) = called.get() {
return val;
}
}
})
} else {
Err(ErrorCode::Fail)
}
}
}

pub trait Config:
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config
{
}

impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config>
Config for T
{
}

#[cfg(test)]
mod tests;

// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------

const DRIVER_NUM: u32 = 0x90001;

// Command IDs
#[allow(unused)]
mod command {
pub const EXISTS: u32 = 0;
pub const SCREEN_SETUP: u32 = 1;
pub const SET_POWER: u32 = 2;
pub const SET_BRIGHTNESS: u32 = 3;
pub const SET_INVERT_ON: u32 = 4;
pub const SET_INVERT_OFF: u32 = 5;
pub const SET_INVERT: u32 = 6;
pub const GET_RESOLUTION_MODES_COUNT: u32 = 11;
pub const GET_RESOLUTION_WIDTH_HEIGHT: u32 = 12;
pub const PIXEL_MODES_COUNT: u32 = 13;
pub const PIXEL_FORMAT: u32 = 14;
pub const GET_ROTATION: u32 = 21;
pub const SET_ROTATION: u32 = 22;
pub const GET_RESOLUTION: u32 = 23;
pub const SET_RESOLUTION: u32 = 24;
pub const GET_PIXEL_FORMAT: u32 = 25;
pub const SET_PIXEL_FORMAT: u32 = 26;
pub const SET_WRITE_FRAME: u32 = 100;
pub const WRITE: u32 = 200;
pub const FILL: u32 = 300;
}

// Subscribe ID used for callbacks
mod subscribe {
pub const WRITE: u32 = 0;
}

// Allow-readonly buffer ID
mod allow_ro {
pub const WRITE_BUFFER_ID: u32 = 0;
}
Loading
Loading