240 lines
7.2 KiB
Rust
240 lines
7.2 KiB
Rust
use core::mem::MaybeUninit;
|
|
|
|
use alloc::{boxed::Box, sync::Arc};
|
|
use async_trait::async_trait;
|
|
use device_api::device::Device;
|
|
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider, L3_PAGE_SIZE};
|
|
use yggdrasil_abi::{
|
|
bitflags,
|
|
error::Error,
|
|
io::device::{self, DeviceRequestVariant, Framebuffer, VideoRequestVariant},
|
|
option::RequestValue,
|
|
process::ProcessId,
|
|
};
|
|
|
|
pub mod access;
|
|
pub mod color;
|
|
pub mod console;
|
|
pub mod fb_console;
|
|
pub mod font;
|
|
|
|
pub use color::Color;
|
|
|
|
use crate::{dma::DmaBuffer, task::thread::Thread};
|
|
|
|
use super::block::BlockDevice;
|
|
|
|
bitflags! {
|
|
/// Init-time display driver flags
|
|
pub struct DriverFlags: u32 {
|
|
/// If set, replaces a previosly present boot framebuffer if one was present
|
|
const REPLACES_BOOT_FRAMEBUFFER: bit 0;
|
|
}
|
|
}
|
|
|
|
/// Represents the current lock holder for a display device
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum DisplayOwner {
|
|
/// Display is not locked
|
|
None,
|
|
/// Kernel is using the display
|
|
Kernel,
|
|
/// Userspace process is using the display
|
|
Process(ProcessId),
|
|
}
|
|
|
|
/// Resolution of the display device
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct DisplayDimensions {
|
|
/// Width of the display in pixels
|
|
pub width: u32,
|
|
/// Height of the display in pixels
|
|
pub height: u32,
|
|
}
|
|
|
|
/// Describes how pixels are laid out in the framebuffer used by a display
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[allow(missing_docs)]
|
|
pub enum PixelFormat {
|
|
B8G8R8A8,
|
|
B8G8R8X8,
|
|
A8R8G8B8,
|
|
X8R8G8B8,
|
|
R8G8B8A8,
|
|
X8B8G8R8,
|
|
A8B8G8R8,
|
|
R8G8B8X8,
|
|
}
|
|
|
|
/// Describes a display mode
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct DisplayMode {
|
|
/// Index for setting this display mode
|
|
pub index: u32,
|
|
/// Width in pixels
|
|
pub width: u32,
|
|
/// Height in pixels
|
|
pub height: u32,
|
|
/// Max frames per second supported by this mode, [u32::MAX] if unlimited.
|
|
pub frames_per_second: u32,
|
|
/// Pixel format of this mode
|
|
pub pixel_format: PixelFormat,
|
|
}
|
|
|
|
/// Describes display device framebuffer layout
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct FramebufferInfo {
|
|
/// Base address
|
|
pub base: PhysicalAddress,
|
|
/// Kernel-accessible virtual base address
|
|
pub kernel_base: Option<usize>,
|
|
/// Stride between the start of one row and the start of the next one.
|
|
pub stride: usize,
|
|
/// Overall size of the framebuffer
|
|
pub size: usize,
|
|
}
|
|
|
|
/// Abstract display device interface
|
|
pub trait DisplayDevice: Device + PageProvider {
|
|
/// Returns the init-time driver flags
|
|
fn driver_flags(&self) -> DriverFlags;
|
|
|
|
/// Returns the active display mode
|
|
fn active_display_mode(&self) -> Option<DisplayMode>;
|
|
/// Returns the list of available display modes
|
|
fn query_display_modes(&self, modes: &mut [MaybeUninit<DisplayMode>]) -> Result<usize, Error>;
|
|
|
|
/// Requests the device to set its display mode to the one described by `index`. If
|
|
/// double buffering is supported, `double_buffer` is used to indicate a request to
|
|
/// set up multiple frame buffers
|
|
fn set_display_mode(&self, index: u32, double_buffer: bool) -> Result<(), Error>;
|
|
|
|
/// Reads the currently active framebuffer information. Will return an error with the
|
|
/// framebuffer count if `output` is too small to contain all the framebuffer infos.
|
|
fn active_framebuffers(
|
|
&self,
|
|
output: &mut [MaybeUninit<FramebufferInfo>],
|
|
) -> Result<usize, usize>;
|
|
|
|
/// Returns the currently active framebuffer, if there is exactly one. Returns an error
|
|
/// otherwise.
|
|
fn active_framebuffer(&self) -> Result<FramebufferInfo, usize> {
|
|
let mut buffer = [MaybeUninit::uninit()];
|
|
if self.active_framebuffers(&mut buffer)? == 1 {
|
|
Ok(unsafe { buffer[0].assume_init() })
|
|
} else {
|
|
// No active framebuffer
|
|
Err(0)
|
|
}
|
|
}
|
|
|
|
/// Indicates to the driver that a frame was finished and can be sent to the device.
|
|
fn synchronize(&self) -> Result<(), Error>;
|
|
|
|
/// Locks the display to only be accessible by a single process.
|
|
fn lock(&self, owner: DisplayOwner) -> Result<(), Error>;
|
|
/// Unlocks the display. `owner` describes who this request comes from.
|
|
fn unlock(&self, owner: DisplayOwner) -> Result<(), Error>;
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DisplayWrapper {
|
|
pub device: Arc<dyn DisplayDevice>,
|
|
pub boot: bool,
|
|
}
|
|
|
|
impl DisplayWrapper {
|
|
pub fn new(device: Arc<dyn DisplayDevice>, boot: bool) -> Result<Self, Error> {
|
|
Ok(Self { device, boot })
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl BlockDevice for DisplayWrapper {
|
|
fn allocate_buffer(&self, _size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
|
Err(Error::NotImplemented)
|
|
}
|
|
|
|
async fn read(&self, _pos: u64, _buf: &mut [u8]) -> Result<usize, Error> {
|
|
Err(Error::InvalidOperation)
|
|
}
|
|
|
|
async fn write(&self, _pos: u64, _buf: &[u8]) -> Result<usize, Error> {
|
|
Err(Error::InvalidOperation)
|
|
}
|
|
|
|
fn device_request(&self, option: u32, buffer: &mut [u8], len: usize) -> Result<usize, Error> {
|
|
let _ = len;
|
|
|
|
if let Ok(option) = DeviceRequestVariant::try_from(option) {
|
|
let process = Thread::current().process_id();
|
|
return match option {
|
|
DeviceRequestVariant::AcquireDevice => {
|
|
self.lock(DisplayOwner::Process(process))?;
|
|
device::AcquireDevice::store_response(&(), buffer)
|
|
}
|
|
DeviceRequestVariant::ReleaseDevice => {
|
|
self.unlock(DisplayOwner::Process(process))?;
|
|
device::ReleaseDevice::store_response(&(), buffer)
|
|
}
|
|
};
|
|
}
|
|
|
|
let option = VideoRequestVariant::try_from(option)?;
|
|
match option {
|
|
VideoRequestVariant::GetActiveFramebuffer => {
|
|
let mode = self.active_display_mode().ok_or(Error::DoesNotExist)?;
|
|
let info = self.active_framebuffer().map_err(|_| Error::DoesNotExist)?;
|
|
let framebuffer = Framebuffer {
|
|
width: mode.width,
|
|
height: mode.height,
|
|
size: info.size,
|
|
stride: info.stride,
|
|
};
|
|
device::GetActiveFramebuffer::store_response(&framebuffer, buffer)
|
|
}
|
|
VideoRequestVariant::FlushDisplay => {
|
|
self.synchronize()?;
|
|
device::FlushDisplay::store_response(&(), buffer)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn block_size(&self) -> usize {
|
|
L3_PAGE_SIZE
|
|
}
|
|
|
|
fn block_count(&self) -> u64 {
|
|
0
|
|
}
|
|
|
|
fn max_blocks_per_request(&self) -> usize {
|
|
0
|
|
}
|
|
}
|
|
|
|
impl PageProvider for DisplayWrapper {
|
|
fn clone_page(
|
|
&self,
|
|
offset: u64,
|
|
src_phys: PhysicalAddress,
|
|
src_attrs: MapAttributes,
|
|
) -> Result<PhysicalAddress, Error> {
|
|
self.device.clone_page(offset, src_phys, src_attrs)
|
|
}
|
|
|
|
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
|
|
self.device.get_page(offset)
|
|
}
|
|
|
|
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> {
|
|
self.device.release_page(offset, phys)
|
|
}
|
|
}
|
|
|
|
impl Device for DisplayWrapper {
|
|
fn display_name(&self) -> &str {
|
|
self.device.display_name()
|
|
}
|
|
}
|