mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 23:47:45 +00:00
Kernel: Introduce a new graphics subsystem
This new subsystem is replacing the old code that was used to create device nodes of framebuffer devices in /dev. This subsystem includes for now 3 roles: 1. GraphicsManagement singleton object that is used in the boot process to enumerate and initialize display devices. 2. GraphicsDevice(s) that are used to control the display adapter. 3. FramebufferDevice(s) that are used to control the device node in /dev. For now, we support the Bochs display adapter and any other generic VGA compatible adapter that was configured by the boot loader to a known and fixed resolution. Two improvements in the Bochs display adapter code are that we can support native bochs-display device (this device doesn't expose any VGA capabilities) and also that we use the MMIO region, to configure the device, instead of setting IO ports for such tasks.
This commit is contained in:
parent
8515a1c49d
commit
6a728e2d76
18 changed files with 672 additions and 428 deletions
|
@ -1,290 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Singleton.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/BXVGADevice.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/PCI/Access.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/VM/AnonymousVMObject.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <Kernel/VM/TypedMapping.h>
|
||||
#include <LibC/errno_numbers.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define MAX_RESOLUTION_WIDTH 4096
|
||||
#define MAX_RESOLUTION_HEIGHT 2160
|
||||
|
||||
#define VBE_DISPI_IOPORT_INDEX 0x01CE
|
||||
#define VBE_DISPI_IOPORT_DATA 0x01CF
|
||||
|
||||
#define VBE_DISPI_INDEX_ID 0x0
|
||||
#define VBE_DISPI_INDEX_XRES 0x1
|
||||
#define VBE_DISPI_INDEX_YRES 0x2
|
||||
#define VBE_DISPI_INDEX_BPP 0x3
|
||||
#define VBE_DISPI_INDEX_ENABLE 0x4
|
||||
#define VBE_DISPI_INDEX_BANK 0x5
|
||||
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
|
||||
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
|
||||
#define VBE_DISPI_INDEX_X_OFFSET 0x8
|
||||
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
|
||||
#define VBE_DISPI_DISABLED 0x00
|
||||
#define VBE_DISPI_ENABLED 0x01
|
||||
#define VBE_DISPI_LFB_ENABLED 0x40
|
||||
|
||||
static AK::Singleton<BXVGADevice> s_the;
|
||||
|
||||
UNMAP_AFTER_INIT void BXVGADevice::initialize()
|
||||
{
|
||||
s_the.ensure_instance();
|
||||
}
|
||||
|
||||
BXVGADevice& BXVGADevice::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT BXVGADevice::BXVGADevice()
|
||||
: BlockDevice(29, 0)
|
||||
{
|
||||
m_framebuffer_address = PhysicalAddress(find_framebuffer_address());
|
||||
m_mmio_registers = find_mmio_region();
|
||||
m_vga_compatible = is_vga_compatible();
|
||||
|
||||
set_register(VBE_DISPI_INDEX_ID, 0xB0C0);
|
||||
dmesgln("BXVGA: ID {}", get_register(VBE_DISPI_INDEX_ID));
|
||||
set_safe_resolution();
|
||||
}
|
||||
|
||||
void BXVGADevice::set_safe_resolution()
|
||||
{
|
||||
m_framebuffer_width = 1024;
|
||||
m_framebuffer_height = 768;
|
||||
m_framebuffer_pitch = m_framebuffer_width * sizeof(u32);
|
||||
set_resolution(m_framebuffer_width, m_framebuffer_height);
|
||||
}
|
||||
|
||||
void BXVGADevice::set_register(u16 index, u16 data)
|
||||
{
|
||||
if (m_vga_compatible) {
|
||||
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
IO::out16(VBE_DISPI_IOPORT_DATA, data);
|
||||
return;
|
||||
}
|
||||
auto reg = map_typed_writable<u16>(m_mmio_registers.offset(index * 2));
|
||||
*(reg.ptr()) = data;
|
||||
}
|
||||
|
||||
u16 BXVGADevice::get_register(u16 index)
|
||||
{
|
||||
if (m_vga_compatible) {
|
||||
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
return IO::in16(VBE_DISPI_IOPORT_DATA);
|
||||
}
|
||||
auto reg = map_typed_writable<u16>(m_mmio_registers.offset(index * 2));
|
||||
return *(reg.ptr());
|
||||
}
|
||||
|
||||
void BXVGADevice::revert_resolution()
|
||||
{
|
||||
set_resolution_registers(m_framebuffer_width, m_framebuffer_height);
|
||||
VERIFY(validate_setup_resolution(m_framebuffer_width, m_framebuffer_height));
|
||||
}
|
||||
|
||||
void BXVGADevice::set_resolution_registers(size_t width, size_t height)
|
||||
{
|
||||
dbgln_if(BXVGA_DEBUG, "BXVGADevice resolution registers set to - {}x{}", width, height);
|
||||
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
||||
set_register(VBE_DISPI_INDEX_XRES, (u16)width);
|
||||
set_register(VBE_DISPI_INDEX_YRES, (u16)height);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (u16)width);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (u16)height * 2);
|
||||
set_register(VBE_DISPI_INDEX_BPP, 32);
|
||||
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
|
||||
set_register(VBE_DISPI_INDEX_BANK, 0);
|
||||
}
|
||||
|
||||
bool BXVGADevice::test_resolution(size_t width, size_t height)
|
||||
{
|
||||
dbgln_if(BXVGA_DEBUG, "BXVGADevice resolution test - {}x{}", width, height);
|
||||
set_resolution_registers(width, height);
|
||||
bool resolution_changed = validate_setup_resolution(width, height);
|
||||
revert_resolution();
|
||||
return resolution_changed;
|
||||
}
|
||||
bool BXVGADevice::set_resolution(size_t width, size_t height)
|
||||
{
|
||||
if (Checked<size_t>::multiplication_would_overflow(width, height, sizeof(u32)))
|
||||
return false;
|
||||
|
||||
if (!test_resolution(width, height))
|
||||
return false;
|
||||
|
||||
set_resolution_registers(width, height);
|
||||
dbgln("BXVGADevice resolution set to {}x{} (pitch={})", width, height, m_framebuffer_pitch);
|
||||
|
||||
m_framebuffer_width = width;
|
||||
m_framebuffer_height = height;
|
||||
m_framebuffer_pitch = width * sizeof(u32);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BXVGADevice::validate_setup_resolution(size_t width, size_t height)
|
||||
{
|
||||
if ((u16)width != get_register(VBE_DISPI_INDEX_XRES) || (u16)height != get_register(VBE_DISPI_INDEX_YRES)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BXVGADevice::set_y_offset(size_t y_offset)
|
||||
{
|
||||
VERIFY(y_offset == 0 || y_offset == m_framebuffer_height);
|
||||
m_y_offset = y_offset;
|
||||
set_register(VBE_DISPI_INDEX_Y_OFFSET, (u16)y_offset);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT u32 BXVGADevice::find_framebuffer_address()
|
||||
{
|
||||
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
|
||||
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
|
||||
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
|
||||
u32 framebuffer_address = 0;
|
||||
PCI::enumerate([&framebuffer_address](const PCI::Address& address, PCI::ID id) {
|
||||
if (id == bochs_vga_id || id == virtualbox_vga_id) {
|
||||
framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0;
|
||||
dbgln("BXVGA: framebuffer @ {}", PhysicalAddress(framebuffer_address));
|
||||
}
|
||||
});
|
||||
return framebuffer_address;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT PhysicalAddress BXVGADevice::find_mmio_region()
|
||||
{
|
||||
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
|
||||
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
|
||||
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
|
||||
u32 mmio_region = 0;
|
||||
PCI::enumerate([&mmio_region](const PCI::Address& address, PCI::ID id) {
|
||||
if (id == bochs_vga_id || id == virtualbox_vga_id) {
|
||||
mmio_region = PCI::get_BAR1(address) & 0xfffffff0;
|
||||
dmesgln("BXVGA: mmio region @ {}", PhysicalAddress(mmio_region));
|
||||
}
|
||||
});
|
||||
return PhysicalAddress(mmio_region);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT bool BXVGADevice::is_vga_compatible()
|
||||
{
|
||||
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
|
||||
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
|
||||
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
|
||||
bool vga_compatible = true;
|
||||
PCI::enumerate([&vga_compatible](const PCI::Address& address, PCI::ID id) {
|
||||
if (id == bochs_vga_id || id == virtualbox_vga_id) {
|
||||
if (PCI::get_subclass(address) != 0)
|
||||
vga_compatible = false;
|
||||
}
|
||||
});
|
||||
return vga_compatible;
|
||||
}
|
||||
|
||||
KResultOr<Region*> BXVGADevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
|
||||
{
|
||||
REQUIRE_PROMISE(video);
|
||||
if (!shared)
|
||||
return ENODEV;
|
||||
if (offset != 0)
|
||||
return ENXIO;
|
||||
if (range.size() != page_round_up(framebuffer_size_in_bytes()))
|
||||
return EOVERFLOW;
|
||||
|
||||
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
|
||||
if (!vmobject)
|
||||
return ENOMEM;
|
||||
return process.space().allocate_region_with_vmobject(
|
||||
range,
|
||||
vmobject.release_nonnull(),
|
||||
0,
|
||||
"BXVGA Framebuffer",
|
||||
prot,
|
||||
shared);
|
||||
}
|
||||
|
||||
String BXVGADevice::device_name() const
|
||||
{
|
||||
return String::formatted("fb{}", minor());
|
||||
}
|
||||
|
||||
int BXVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
||||
{
|
||||
REQUIRE_PROMISE(video);
|
||||
switch (request) {
|
||||
case FB_IOCTL_GET_SIZE_IN_BYTES: {
|
||||
auto* out = (size_t*)arg;
|
||||
size_t value = framebuffer_size_in_bytes();
|
||||
if (!copy_to_user(out, &value))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_GET_BUFFER: {
|
||||
auto* index = (int*)arg;
|
||||
int value = m_y_offset == 0 ? 0 : 1;
|
||||
if (!copy_to_user(index, &value))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_SET_BUFFER: {
|
||||
if (arg != 0 && arg != 1)
|
||||
return -EINVAL;
|
||||
set_y_offset(arg == 0 ? 0 : m_framebuffer_height);
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_GET_RESOLUTION: {
|
||||
auto* user_resolution = (FBResolution*)arg;
|
||||
FBResolution resolution;
|
||||
resolution.pitch = m_framebuffer_pitch;
|
||||
resolution.width = m_framebuffer_width;
|
||||
resolution.height = m_framebuffer_height;
|
||||
if (!copy_to_user(user_resolution, &resolution))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_SET_RESOLUTION: {
|
||||
auto* user_resolution = (FBResolution*)arg;
|
||||
FBResolution resolution;
|
||||
if (!copy_from_user(&resolution, user_resolution))
|
||||
return -EFAULT;
|
||||
if (resolution.width > MAX_RESOLUTION_WIDTH || resolution.height > MAX_RESOLUTION_HEIGHT)
|
||||
return -EINVAL;
|
||||
if (!set_resolution(resolution.width, resolution.height)) {
|
||||
dbgln_if(BXVGA_DEBUG, "Reverting resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
|
||||
resolution.pitch = m_framebuffer_pitch;
|
||||
resolution.width = m_framebuffer_width;
|
||||
resolution.height = m_framebuffer_height;
|
||||
if (!copy_to_user(user_resolution, &resolution))
|
||||
return -EFAULT;
|
||||
return -EINVAL;
|
||||
}
|
||||
dbgln_if(BXVGA_DEBUG, "New resolution: [{}x{}]", m_framebuffer_width, m_framebuffer_height);
|
||||
resolution.pitch = m_framebuffer_pitch;
|
||||
resolution.width = m_framebuffer_width;
|
||||
resolution.height = m_framebuffer_height;
|
||||
if (!copy_to_user(user_resolution, &resolution))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/BlockDevice.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class BXVGADevice final : public BlockDevice {
|
||||
AK_MAKE_ETERNAL
|
||||
public:
|
||||
static void initialize();
|
||||
static BXVGADevice& the();
|
||||
|
||||
BXVGADevice();
|
||||
|
||||
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
|
||||
virtual KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override;
|
||||
|
||||
// ^Device
|
||||
virtual mode_t required_mode() const override { return 0660; }
|
||||
virtual String device_name() const override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "BXVGA"; }
|
||||
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
|
||||
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
|
||||
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
||||
|
||||
PhysicalAddress find_mmio_region();
|
||||
bool is_vga_compatible();
|
||||
|
||||
void set_safe_resolution();
|
||||
|
||||
void set_register(u16 index, u16 value);
|
||||
u16 get_register(u16 index);
|
||||
bool validate_setup_resolution(size_t width, size_t height);
|
||||
u32 find_framebuffer_address();
|
||||
void revert_resolution();
|
||||
bool test_resolution(size_t width, size_t height);
|
||||
size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height * 2; }
|
||||
bool set_resolution(size_t width, size_t height);
|
||||
void set_resolution_registers(size_t width, size_t height);
|
||||
void set_y_offset(size_t);
|
||||
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
PhysicalAddress m_mmio_registers;
|
||||
bool m_vga_compatible { true };
|
||||
size_t m_framebuffer_pitch { 0 };
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
size_t m_y_offset { 0 };
|
||||
};
|
||||
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/MBVGADevice.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/VM/AnonymousVMObject.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <LibC/errno_numbers.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
static MBVGADevice* s_the;
|
||||
|
||||
MBVGADevice& MBVGADevice::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT MBVGADevice::MBVGADevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height)
|
||||
: BlockDevice(29, 0)
|
||||
, m_framebuffer_address(addr)
|
||||
, m_framebuffer_pitch(pitch)
|
||||
, m_framebuffer_width(width)
|
||||
, m_framebuffer_height(height)
|
||||
{
|
||||
dbgln("MBVGADevice address={}, pitch={}, width={}, height={}", addr, pitch, width, height);
|
||||
s_the = this;
|
||||
}
|
||||
|
||||
KResultOr<Region*> MBVGADevice::mmap(Process& process, FileDescription&, const Range& range, u64 offset, int prot, bool shared)
|
||||
{
|
||||
REQUIRE_PROMISE(video);
|
||||
if (!shared)
|
||||
return ENODEV;
|
||||
if (offset != 0)
|
||||
return ENXIO;
|
||||
if (range.size() != page_round_up(framebuffer_size_in_bytes()))
|
||||
return EOVERFLOW;
|
||||
|
||||
auto vmobject = AnonymousVMObject::create_for_physical_range(m_framebuffer_address, framebuffer_size_in_bytes());
|
||||
if (!vmobject)
|
||||
return ENOMEM;
|
||||
return process.space().allocate_region_with_vmobject(
|
||||
range,
|
||||
vmobject.release_nonnull(),
|
||||
0,
|
||||
"MBVGA Framebuffer",
|
||||
prot,
|
||||
shared);
|
||||
}
|
||||
|
||||
int MBVGADevice::ioctl(FileDescription&, unsigned request, FlatPtr arg)
|
||||
{
|
||||
REQUIRE_PROMISE(video);
|
||||
switch (request) {
|
||||
case FB_IOCTL_GET_SIZE_IN_BYTES: {
|
||||
auto* out = (size_t*)arg;
|
||||
size_t value = framebuffer_size_in_bytes();
|
||||
if (!copy_to_user(out, &value))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_GET_BUFFER: {
|
||||
auto* index = (int*)arg;
|
||||
int value = 0;
|
||||
if (!copy_to_user(index, &value))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_GET_RESOLUTION: {
|
||||
auto* user_resolution = (FBResolution*)arg;
|
||||
FBResolution resolution;
|
||||
resolution.pitch = m_framebuffer_pitch;
|
||||
resolution.width = m_framebuffer_width;
|
||||
resolution.height = m_framebuffer_height;
|
||||
if (!copy_to_user(user_resolution, &resolution))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case FB_IOCTL_SET_RESOLUTION: {
|
||||
auto* user_resolution = (FBResolution*)arg;
|
||||
FBResolution resolution;
|
||||
resolution.pitch = m_framebuffer_pitch;
|
||||
resolution.width = m_framebuffer_width;
|
||||
resolution.height = m_framebuffer_height;
|
||||
if (!copy_to_user(user_resolution, &resolution))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
String MBVGADevice::device_name() const
|
||||
{
|
||||
return String::formatted("fb{}", minor());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/BlockDevice.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class MBVGADevice final : public BlockDevice {
|
||||
AK_MAKE_ETERNAL
|
||||
public:
|
||||
static MBVGADevice& the();
|
||||
|
||||
MBVGADevice(PhysicalAddress addr, size_t pitch, size_t width, size_t height);
|
||||
|
||||
virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
|
||||
virtual KResultOr<Region*> mmap(Process&, FileDescription&, const Range&, u64 offset, int prot, bool shared) override;
|
||||
|
||||
// ^Device
|
||||
virtual mode_t required_mode() const override { return 0660; }
|
||||
virtual String device_name() const override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "MBVGA"; }
|
||||
virtual bool can_read(const FileDescription&, size_t) const override { return true; }
|
||||
virtual bool can_write(const FileDescription&, size_t) const override { return true; }
|
||||
virtual KResultOr<size_t> read(FileDescription&, u64, UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual KResultOr<size_t> write(FileDescription&, u64, const UserOrKernelBuffer&, size_t) override { return -EINVAL; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); }
|
||||
|
||||
size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; }
|
||||
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
size_t m_framebuffer_pitch { 0 };
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue