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()
}
}