mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 09:57:35 +00:00
Everywhere: Purge all support and usage of framebuffer devices
Long live the DisplayConnector object!
This commit is contained in:
parent
aad968cc5e
commit
e301af8352
25 changed files with 90 additions and 995 deletions
|
@ -18,32 +18,6 @@ ALWAYS_INLINE int graphics_connector_get_properties(int fd, GraphicsConnectorPro
|
|||
return ioctl(fd, GRAPHICS_IOCTL_GET_PROPERTIES, info);
|
||||
}
|
||||
|
||||
// FIXME: Remove this once framebuffer devices are removed.
|
||||
ALWAYS_INLINE int fb_get_head_properties(int fd, FBHeadProperties* info)
|
||||
{
|
||||
return ioctl(fd, GRAPHICS_IOCTL_GET_HEAD_PROPERTIES, info);
|
||||
}
|
||||
|
||||
// FIXME: Remove this once framebuffer devices are removed.
|
||||
ALWAYS_INLINE int fb_get_resolution(int fd, FBHeadResolution* info)
|
||||
{
|
||||
FBHeadProperties head_properties;
|
||||
head_properties.head_index = info->head_index;
|
||||
if (auto rc = ioctl(fd, GRAPHICS_IOCTL_GET_HEAD_PROPERTIES, &head_properties); rc < 0)
|
||||
return rc;
|
||||
info->head_index = head_properties.head_index;
|
||||
info->pitch = head_properties.pitch;
|
||||
info->width = head_properties.width;
|
||||
info->height = head_properties.height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME: Remove this once framebuffer devices are removed.
|
||||
ALWAYS_INLINE int fb_set_resolution(int fd, FBHeadResolution* info)
|
||||
{
|
||||
return ioctl(fd, GRAPHICS_IOCTL_SET_HEAD_RESOLUTION, info);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE int graphics_connector_get_head_edid(int fd, GraphicsHeadEDID* info)
|
||||
{
|
||||
return ioctl(fd, GRAPHICS_IOCTL_GET_HEAD_EDID, info);
|
||||
|
|
|
@ -83,7 +83,6 @@ set(KERNEL_SOURCES
|
|||
Graphics/Console/TextModeConsole.cpp
|
||||
Graphics/Console/VGAConsole.cpp
|
||||
Graphics/DisplayConnector.cpp
|
||||
Graphics/FramebufferDevice.cpp
|
||||
Graphics/GraphicsManagement.cpp
|
||||
Graphics/Intel/NativeDisplayConnector.cpp
|
||||
Graphics/Intel/NativeGraphicsAdapter.cpp
|
||||
|
@ -95,7 +94,6 @@ set(KERNEL_SOURCES
|
|||
Graphics/VirtIOGPU/Console.cpp
|
||||
Graphics/VirtIOGPU/GPU3DDevice.cpp
|
||||
Graphics/VirtIOGPU/GraphicsAdapter.cpp
|
||||
Graphics/GenericFramebufferDevice.cpp
|
||||
SanCov.cpp
|
||||
Storage/ATA/AHCIController.cpp
|
||||
Storage/ATA/AHCIPort.cpp
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Graphics/Bochs/Definitions.h>
|
||||
#include <Kernel/Graphics/Console/GenericFramebufferConsole.h>
|
||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Try.h>
|
||||
#include <Kernel/API/POSIX/errno.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
NonnullRefPtr<FramebufferDevice> FramebufferDevice::create(GenericGraphicsAdapter const& adapter, PhysicalAddress paddr, size_t width, size_t height, size_t pitch)
|
||||
{
|
||||
auto framebuffer_device_or_error = DeviceManagement::try_create_device<FramebufferDevice>(adapter, paddr, width, height, pitch);
|
||||
// FIXME: Find a way to propagate errors
|
||||
VERIFY(!framebuffer_device_or_error.is_error());
|
||||
return framebuffer_device_or_error.release_value();
|
||||
}
|
||||
|
||||
ErrorOr<Memory::Region*> FramebufferDevice::mmap(Process& process, OpenFileDescription&, Memory::VirtualRange const& range, u64 offset, int prot, bool shared)
|
||||
{
|
||||
TRY(process.require_promise(Pledge::video));
|
||||
SpinlockLocker lock(m_activation_lock);
|
||||
if (!shared)
|
||||
return ENODEV;
|
||||
if (offset != 0)
|
||||
return ENXIO;
|
||||
auto framebuffer_length = TRY(buffer_length(0));
|
||||
framebuffer_length = TRY(Memory::page_round_up(framebuffer_length));
|
||||
if (range.size() != framebuffer_length)
|
||||
return EOVERFLOW;
|
||||
|
||||
m_userspace_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, framebuffer_length));
|
||||
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, framebuffer_length));
|
||||
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(framebuffer_length, AllocationStrategy::AllocateNow));
|
||||
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, framebuffer_length, "Framebuffer", Memory::Region::Access::ReadWrite));
|
||||
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, framebuffer_length, "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
||||
|
||||
RefPtr<Memory::VMObject> chosen_vmobject;
|
||||
if (m_graphical_writes_enabled) {
|
||||
chosen_vmobject = m_real_framebuffer_vmobject;
|
||||
} else {
|
||||
chosen_vmobject = m_swapped_framebuffer_vmobject;
|
||||
}
|
||||
m_userspace_framebuffer_region = TRY(process.address_space().allocate_region_with_vmobject(
|
||||
range,
|
||||
chosen_vmobject.release_nonnull(),
|
||||
0,
|
||||
"Framebuffer",
|
||||
prot,
|
||||
shared));
|
||||
if (m_write_combine) {
|
||||
if (auto result = m_userspace_framebuffer_region->set_write_combine(true); result.is_error())
|
||||
dbgln("FramebufferDevice: Failed to enable Write-Combine on Framebuffer: {}", result.error());
|
||||
}
|
||||
return m_userspace_framebuffer_region;
|
||||
}
|
||||
|
||||
void FramebufferDevice::deactivate_writes()
|
||||
{
|
||||
SpinlockLocker lock(m_activation_lock);
|
||||
if (!m_userspace_framebuffer_region)
|
||||
return;
|
||||
auto framebuffer_length_or_error = buffer_length(0);
|
||||
VERIFY(!framebuffer_length_or_error.is_error());
|
||||
size_t rounded_framebuffer_length = Memory::page_round_up(framebuffer_length_or_error.release_value()).release_value_but_fixme_should_propagate_errors();
|
||||
memcpy(m_swapped_framebuffer_region->vaddr().as_ptr(), m_real_framebuffer_region->vaddr().as_ptr(), rounded_framebuffer_length);
|
||||
auto vmobject = m_swapped_framebuffer_vmobject;
|
||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||
m_userspace_framebuffer_region->remap();
|
||||
m_graphical_writes_enabled = false;
|
||||
}
|
||||
void FramebufferDevice::activate_writes()
|
||||
{
|
||||
SpinlockLocker lock(m_activation_lock);
|
||||
if (!m_userspace_framebuffer_region || !m_real_framebuffer_vmobject)
|
||||
return;
|
||||
// restore the image we had in the void area
|
||||
// FIXME: if we happen to have multiple Framebuffers that are writing to that location
|
||||
// we will experience glitches...
|
||||
auto framebuffer_length_or_error = buffer_length(0);
|
||||
VERIFY(!framebuffer_length_or_error.is_error());
|
||||
|
||||
size_t rounded_framebuffer_length = Memory::page_round_up(framebuffer_length_or_error.release_value()).release_value_but_fixme_should_propagate_errors();
|
||||
memcpy(m_real_framebuffer_region->vaddr().as_ptr(), m_swapped_framebuffer_region->vaddr().as_ptr(), rounded_framebuffer_length);
|
||||
auto vmobject = m_userspace_real_framebuffer_vmobject;
|
||||
m_userspace_framebuffer_region->set_vmobject(vmobject.release_nonnull());
|
||||
m_userspace_framebuffer_region->remap();
|
||||
m_graphical_writes_enabled = true;
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT ErrorOr<void> FramebufferDevice::try_to_initialize()
|
||||
{
|
||||
// FIXME: Would be nice to be able to unify this with mmap above, but this
|
||||
// function is UNMAP_AFTER_INIT for the time being.
|
||||
auto framebuffer_length = TRY(buffer_length(0));
|
||||
framebuffer_length = TRY(Memory::page_round_up(framebuffer_length));
|
||||
m_real_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_for_physical_range(m_framebuffer_address, framebuffer_length));
|
||||
m_swapped_framebuffer_vmobject = TRY(Memory::AnonymousVMObject::try_create_with_size(framebuffer_length, AllocationStrategy::AllocateNow));
|
||||
m_real_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_real_framebuffer_vmobject, framebuffer_length, "Framebuffer", Memory::Region::Access::ReadWrite));
|
||||
m_swapped_framebuffer_region = TRY(MM.allocate_kernel_region_with_vmobject(*m_swapped_framebuffer_vmobject, framebuffer_length, "Framebuffer Swap (Blank)", Memory::Region::Access::ReadWrite));
|
||||
return {};
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT FramebufferDevice::FramebufferDevice(GenericGraphicsAdapter const& adapter, PhysicalAddress addr, size_t width, size_t height, size_t pitch)
|
||||
: GenericFramebufferDevice(adapter)
|
||||
, m_framebuffer_address(addr)
|
||||
, m_framebuffer_pitch(pitch)
|
||||
, m_framebuffer_width(width)
|
||||
, m_framebuffer_height(height)
|
||||
{
|
||||
VERIFY(!m_framebuffer_address.is_null());
|
||||
VERIFY(m_framebuffer_pitch);
|
||||
VERIFY(m_framebuffer_width);
|
||||
VERIFY(m_framebuffer_height);
|
||||
dbgln("Framebuffer {}: address={}, pitch={}, width={}, height={}", minor(), addr, pitch, width, height);
|
||||
}
|
||||
|
||||
ErrorOr<size_t> FramebufferDevice::buffer_length(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_resolution_lock);
|
||||
auto adapter = m_graphics_adapter.strong_ref();
|
||||
if (!adapter)
|
||||
return Error::from_errno(EIO);
|
||||
if (adapter->double_framebuffering_capable())
|
||||
return m_framebuffer_pitch * m_framebuffer_height * 2;
|
||||
return m_framebuffer_pitch * m_framebuffer_height;
|
||||
}
|
||||
|
||||
ErrorOr<size_t> FramebufferDevice::pitch(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_resolution_lock);
|
||||
return m_framebuffer_pitch;
|
||||
}
|
||||
ErrorOr<size_t> FramebufferDevice::height(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_resolution_lock);
|
||||
return m_framebuffer_height;
|
||||
}
|
||||
ErrorOr<size_t> FramebufferDevice::width(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_resolution_lock);
|
||||
return m_framebuffer_width;
|
||||
}
|
||||
ErrorOr<size_t> FramebufferDevice::vertical_offset(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_buffer_offset_lock);
|
||||
return m_y_offset;
|
||||
}
|
||||
ErrorOr<bool> FramebufferDevice::vertical_offsetted(size_t head) const
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_buffer_offset_lock);
|
||||
return m_y_offset == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
ErrorOr<void> FramebufferDevice::set_head_resolution(size_t head, size_t width, size_t height, size_t)
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker buffer_offset_locker(m_buffer_offset_lock);
|
||||
SpinlockLocker resolution_locker(m_resolution_lock);
|
||||
auto adapter = m_graphics_adapter.strong_ref();
|
||||
if (!adapter)
|
||||
return Error::from_errno(EIO);
|
||||
auto result = adapter->try_to_set_resolution(0, width, height);
|
||||
// FIXME: Find a better way to return here a ErrorOr<void>.
|
||||
if (!result)
|
||||
return Error::from_errno(ENOTSUP);
|
||||
m_framebuffer_width = width;
|
||||
m_framebuffer_height = height;
|
||||
m_framebuffer_pitch = width * sizeof(u32);
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> FramebufferDevice::set_head_buffer(size_t head, bool second_buffer)
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support multihead setup.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally have a value different than 0, assert.
|
||||
VERIFY(head == 0);
|
||||
SpinlockLocker locker(m_buffer_offset_lock);
|
||||
auto adapter = m_graphics_adapter.strong_ref();
|
||||
if (!adapter)
|
||||
return Error::from_errno(EIO);
|
||||
if (second_buffer) {
|
||||
if (!adapter->set_y_offset(0, m_framebuffer_height)) {
|
||||
// FIXME: Find a better ErrorOr<void> here.
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
m_y_offset = m_framebuffer_height;
|
||||
} else {
|
||||
if (!adapter->set_y_offset(0, 0)) {
|
||||
// FIXME: Find a better ErrorOr<void> here.
|
||||
return Error::from_errno(ENOTSUP);
|
||||
}
|
||||
m_y_offset = 0;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> FramebufferDevice::flush_head_buffer(size_t)
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support flushing.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally reach this code, assert.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
ErrorOr<void> FramebufferDevice::flush_rectangle(size_t, FBRect const&)
|
||||
{
|
||||
// Note: This FramebufferDevice class doesn't support partial flushing.
|
||||
// We take care to verify this at the GenericFramebufferDevice::ioctl method
|
||||
// so if we happen to accidentally reach this code, assert.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<ByteBuffer> FramebufferDevice::get_edid(size_t head) const
|
||||
{
|
||||
auto adapter = m_graphics_adapter.strong_ref();
|
||||
if (!adapter)
|
||||
return Error::from_errno(EIO);
|
||||
return adapter->get_edid(head);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Graphics/GenericFramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Locking/Spinlock.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class FramebufferDevice final : public GenericFramebufferDevice {
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
static NonnullRefPtr<FramebufferDevice> create(GenericGraphicsAdapter const&, PhysicalAddress, size_t, size_t, size_t);
|
||||
|
||||
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override;
|
||||
|
||||
virtual void deactivate_writes() override;
|
||||
virtual void activate_writes() override;
|
||||
|
||||
virtual ErrorOr<void> try_to_initialize() override;
|
||||
|
||||
virtual bool multihead_support() const override { return false; }
|
||||
virtual bool flushing_support() const override { return false; }
|
||||
virtual bool partial_flushing_support() const override { return false; }
|
||||
virtual size_t heads_count() const override { return 1; }
|
||||
virtual ErrorOr<size_t> buffer_length(size_t head) const override;
|
||||
virtual ErrorOr<size_t> pitch(size_t head) const override;
|
||||
virtual ErrorOr<size_t> height(size_t head) const override;
|
||||
virtual ErrorOr<size_t> width(size_t head) const override;
|
||||
virtual ErrorOr<size_t> vertical_offset(size_t head) const override;
|
||||
virtual ErrorOr<bool> vertical_offsetted(size_t head) const override;
|
||||
|
||||
virtual ErrorOr<ByteBuffer> get_edid(size_t head) const override;
|
||||
|
||||
void enable_write_combine(bool write_combine) { m_write_combine = write_combine; }
|
||||
|
||||
private:
|
||||
virtual ErrorOr<void> set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) override;
|
||||
virtual ErrorOr<void> set_head_buffer(size_t head, bool second_buffer) override;
|
||||
virtual ErrorOr<void> flush_head_buffer(size_t head) override;
|
||||
virtual ErrorOr<void> flush_rectangle(size_t head, FBRect const&) override;
|
||||
|
||||
FramebufferDevice(GenericGraphicsAdapter const&, PhysicalAddress, size_t, size_t, size_t);
|
||||
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
size_t m_framebuffer_pitch { 0 };
|
||||
size_t m_framebuffer_width { 0 };
|
||||
size_t m_framebuffer_height { 0 };
|
||||
|
||||
Spinlock m_activation_lock;
|
||||
mutable Spinlock m_buffer_offset_lock;
|
||||
|
||||
RefPtr<Memory::AnonymousVMObject> m_real_framebuffer_vmobject;
|
||||
RefPtr<Memory::AnonymousVMObject> m_swapped_framebuffer_vmobject;
|
||||
OwnPtr<Memory::Region> m_real_framebuffer_region;
|
||||
OwnPtr<Memory::Region> m_swapped_framebuffer_region;
|
||||
|
||||
bool m_graphical_writes_enabled { true };
|
||||
bool m_write_combine { true };
|
||||
|
||||
RefPtr<Memory::AnonymousVMObject> m_userspace_real_framebuffer_vmobject;
|
||||
Memory::Region* m_userspace_framebuffer_region { nullptr };
|
||||
|
||||
size_t m_y_offset { 0 };
|
||||
};
|
||||
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Checked.h>
|
||||
#include <AK/Try.h>
|
||||
#include <Kernel/API/POSIX/errno.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GraphicsManagement.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/Sections.h>
|
||||
#include <Kernel/StdLib.h>
|
||||
|
||||
#define MAX_RESOLUTION_WIDTH 4096
|
||||
#define MAX_RESOLUTION_HEIGHT 2160
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ErrorOr<void> GenericFramebufferDevice::verify_head_index(int head_index) const
|
||||
{
|
||||
if (head_index < 0)
|
||||
return Error::from_errno(EINVAL);
|
||||
if (!multihead_support() && head_index > 0)
|
||||
return Error::from_errno(ENOTSUP);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> GenericFramebufferDevice::ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg)
|
||||
{
|
||||
if (request != GRAPHICS_IOCTL_GET_HEAD_EDID) {
|
||||
// Allow anyone to query the EDID. Eventually we'll publish the current EDID on /sys
|
||||
// so it doesn't really make sense to require the video pledge to query it.
|
||||
TRY(Process::current().require_promise(Pledge::video));
|
||||
}
|
||||
switch (request) {
|
||||
case GRAPHICS_IOCTL_GET_PROPERTIES: {
|
||||
auto user_properties = static_ptr_cast<GraphicsConnectorProperties*>(arg);
|
||||
GraphicsConnectorProperties properties {};
|
||||
auto adapter = m_graphics_adapter.strong_ref();
|
||||
if (!adapter)
|
||||
return Error::from_errno(EIO);
|
||||
properties.multihead_support = multihead_support();
|
||||
properties.flushing_support = flushing_support();
|
||||
properties.doublebuffer_support = adapter->double_framebuffering_capable();
|
||||
properties.partial_flushing_support = partial_flushing_support();
|
||||
return copy_to_user(user_properties, &properties);
|
||||
}
|
||||
case GRAPHICS_IOCTL_GET_HEAD_PROPERTIES: {
|
||||
auto user_head_properties = static_ptr_cast<FBHeadProperties*>(arg);
|
||||
FBHeadProperties head_properties {};
|
||||
TRY(copy_from_user(&head_properties, user_head_properties));
|
||||
TRY(verify_head_index(head_properties.head_index));
|
||||
|
||||
head_properties.pitch = TRY(pitch(head_properties.head_index));
|
||||
head_properties.width = TRY(width(head_properties.head_index));
|
||||
head_properties.height = TRY(height(head_properties.head_index));
|
||||
head_properties.buffer_length = TRY(buffer_length(head_properties.head_index));
|
||||
head_properties.offset = TRY(vertical_offset(head_properties.head_index));
|
||||
return copy_to_user(user_head_properties, &head_properties);
|
||||
}
|
||||
case GRAPHICS_IOCTL_GET_HEAD_EDID: {
|
||||
auto user_head_edid = static_ptr_cast<GraphicsHeadEDID*>(arg);
|
||||
GraphicsHeadEDID head_edid {};
|
||||
TRY(copy_from_user(&head_edid, user_head_edid));
|
||||
TRY(verify_head_index(head_edid.head_index));
|
||||
|
||||
auto edid_bytes = TRY(get_edid(head_edid.head_index));
|
||||
if (head_edid.bytes != nullptr) {
|
||||
// Only return the EDID if a buffer was provided. Either way,
|
||||
// we'll write back the bytes_size with the actual size
|
||||
if (head_edid.bytes_size < edid_bytes.size()) {
|
||||
head_edid.bytes_size = edid_bytes.size();
|
||||
TRY(copy_to_user(user_head_edid, &head_edid));
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
}
|
||||
TRY(copy_to_user(head_edid.bytes, (void const*)edid_bytes.data(), edid_bytes.size()));
|
||||
}
|
||||
head_edid.bytes_size = edid_bytes.size();
|
||||
return copy_to_user(user_head_edid, &head_edid);
|
||||
}
|
||||
case GRAPHICS_IOCTL_SET_HEAD_RESOLUTION: {
|
||||
auto user_head_resolution = static_ptr_cast<FBHeadResolution const*>(arg);
|
||||
auto head_resolution = TRY(copy_typed_from_user(user_head_resolution));
|
||||
TRY(verify_head_index(head_resolution.head_index));
|
||||
|
||||
if (head_resolution.pitch < 0)
|
||||
return Error::from_errno(EINVAL);
|
||||
if (head_resolution.width < 0)
|
||||
return Error::from_errno(EINVAL);
|
||||
if (head_resolution.height < 0)
|
||||
return Error::from_errno(EINVAL);
|
||||
TRY(set_head_resolution(head_resolution.head_index, head_resolution.width, head_resolution.height, head_resolution.pitch));
|
||||
return {};
|
||||
}
|
||||
case GRAPHICS_IOCTL_SET_HEAD_VERTICAL_OFFSET_BUFFER: {
|
||||
auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset const*>(arg);
|
||||
auto head_vertical_buffer_offset = TRY(copy_typed_from_user(user_head_vertical_buffer_offset));
|
||||
TRY(verify_head_index(head_vertical_buffer_offset.head_index));
|
||||
|
||||
if (head_vertical_buffer_offset.offsetted < 0 || head_vertical_buffer_offset.offsetted > 1)
|
||||
return Error::from_errno(EINVAL);
|
||||
TRY(set_head_buffer(head_vertical_buffer_offset.head_index, head_vertical_buffer_offset.offsetted));
|
||||
return {};
|
||||
}
|
||||
case GRAPHICS_IOCTL_GET_HEAD_VERTICAL_OFFSET_BUFFER: {
|
||||
auto user_head_vertical_buffer_offset = static_ptr_cast<GraphicsHeadVerticalOffset*>(arg);
|
||||
GraphicsHeadVerticalOffset head_vertical_buffer_offset {};
|
||||
TRY(copy_from_user(&head_vertical_buffer_offset, user_head_vertical_buffer_offset));
|
||||
TRY(verify_head_index(head_vertical_buffer_offset.head_index));
|
||||
|
||||
head_vertical_buffer_offset.offsetted = TRY(vertical_offsetted(head_vertical_buffer_offset.head_index));
|
||||
return copy_to_user(user_head_vertical_buffer_offset, &head_vertical_buffer_offset);
|
||||
}
|
||||
case GRAPHICS_IOCTL_FLUSH_HEAD_BUFFERS: {
|
||||
if (!partial_flushing_support())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
auto user_flush_rects = static_ptr_cast<FBFlushRects const*>(arg);
|
||||
auto flush_rects = TRY(copy_typed_from_user(user_flush_rects));
|
||||
if (Checked<unsigned>::multiplication_would_overflow(flush_rects.count, sizeof(FBRect)))
|
||||
return Error::from_errno(EFAULT);
|
||||
SpinlockLocker locker(m_flushing_lock);
|
||||
if (flush_rects.count > 0) {
|
||||
for (unsigned i = 0; i < flush_rects.count; i++) {
|
||||
FBRect user_dirty_rect;
|
||||
TRY(copy_from_user(&user_dirty_rect, &flush_rects.rects[i]));
|
||||
TRY(flush_rectangle(flush_rects.buffer_index, user_dirty_rect));
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
case GRAPHICS_IOCTL_FLUSH_HEAD: {
|
||||
if (!flushing_support())
|
||||
return Error::from_errno(ENOTSUP);
|
||||
// Note: We accept a FBRect, but we only really care about the head_index value.
|
||||
auto user_rect = static_ptr_cast<FBRect const*>(arg);
|
||||
auto rect = TRY(copy_typed_from_user(user_rect));
|
||||
TRY(verify_head_index(rect.head_index));
|
||||
TRY(flush_head_buffer(rect.head_index));
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
return EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
GenericFramebufferDevice::GenericFramebufferDevice(GenericGraphicsAdapter const& adapter)
|
||||
: BlockDevice(29, GraphicsManagement::the().allocate_minor_device_number())
|
||||
, m_graphics_adapter(adapter)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Devices/BlockDevice.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Locking/Spinlock.h>
|
||||
#include <LibC/sys/ioctl_numbers.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class GenericFramebufferDevice : public BlockDevice {
|
||||
friend class DeviceManagement;
|
||||
|
||||
public:
|
||||
virtual ErrorOr<void> try_to_initialize() = 0;
|
||||
|
||||
virtual void deactivate_writes() = 0;
|
||||
virtual void activate_writes() = 0;
|
||||
|
||||
virtual ~GenericFramebufferDevice() = default;
|
||||
|
||||
// ^File
|
||||
virtual ErrorOr<Memory::Region*> mmap(Process&, OpenFileDescription&, Memory::VirtualRange const&, u64 offset, int prot, bool shared) override = 0;
|
||||
virtual ErrorOr<void> ioctl(OpenFileDescription&, unsigned request, Userspace<void*> arg) override final;
|
||||
virtual StringView class_name() const override final { return "FramebufferDevice"sv; }
|
||||
|
||||
private:
|
||||
// ^File
|
||||
virtual bool can_read(OpenFileDescription const&, u64) const override final { return true; }
|
||||
virtual bool can_write(OpenFileDescription const&, u64) const override final { return true; }
|
||||
virtual void start_request(AsyncBlockDeviceRequest& request) override final { request.complete(AsyncDeviceRequest::Failure); }
|
||||
virtual ErrorOr<size_t> read(OpenFileDescription&, u64, UserOrKernelBuffer&, size_t) override { return EINVAL; }
|
||||
virtual ErrorOr<size_t> write(OpenFileDescription&, u64, UserOrKernelBuffer const&, size_t) override { return EINVAL; }
|
||||
|
||||
protected:
|
||||
virtual bool multihead_support() const = 0;
|
||||
virtual bool flushing_support() const = 0;
|
||||
virtual bool partial_flushing_support() const = 0;
|
||||
virtual size_t heads_count() const = 0;
|
||||
virtual ErrorOr<size_t> buffer_length(size_t head) const = 0;
|
||||
virtual ErrorOr<size_t> pitch(size_t head) const = 0;
|
||||
virtual ErrorOr<size_t> height(size_t head) const = 0;
|
||||
virtual ErrorOr<size_t> width(size_t head) const = 0;
|
||||
virtual ErrorOr<size_t> vertical_offset(size_t head) const = 0;
|
||||
virtual ErrorOr<bool> vertical_offsetted(size_t head) const = 0;
|
||||
|
||||
virtual ErrorOr<void> set_head_resolution(size_t head, size_t width, size_t height, size_t pitch) = 0;
|
||||
virtual ErrorOr<void> set_head_buffer(size_t head, bool second_buffer) = 0;
|
||||
virtual ErrorOr<void> flush_head_buffer(size_t head) = 0;
|
||||
// FIXME: This method is too much specific to the VirtIO implementation (especially the buffer_index parameter)
|
||||
virtual ErrorOr<void> flush_rectangle(size_t buffer_index, FBRect const&) = 0;
|
||||
|
||||
virtual ErrorOr<ByteBuffer> get_edid(size_t head) const = 0;
|
||||
|
||||
ErrorOr<void> verify_head_index(int head_index) const;
|
||||
|
||||
GenericFramebufferDevice(GenericGraphicsAdapter const&);
|
||||
mutable WeakPtr<GenericGraphicsAdapter> m_graphics_adapter;
|
||||
mutable Spinlock m_flushing_lock;
|
||||
mutable Spinlock m_resolution_lock;
|
||||
};
|
||||
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/Types.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Graphics/VGA/VGACompatibleAdapter.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/Types.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Graphics/Console/Console.h>
|
||||
#include <Kernel/Graphics/FramebufferDevice.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue