summaryrefslogtreecommitdiff
path: root/Kernel/Devices
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/Devices')
-rw-r--r--Kernel/Devices/BXVGADevice.cpp144
-rw-r--r--Kernel/Devices/BXVGADevice.h38
-rw-r--r--Kernel/Devices/BlockDevice.cpp5
-rw-r--r--Kernel/Devices/BlockDevice.h16
-rw-r--r--Kernel/Devices/CharacterDevice.cpp5
-rw-r--r--Kernel/Devices/CharacterDevice.h14
-rw-r--r--Kernel/Devices/Device.cpp28
-rw-r--r--Kernel/Devices/Device.h51
-rw-r--r--Kernel/Devices/DiskDevice.cpp42
-rw-r--r--Kernel/Devices/DiskDevice.h23
-rw-r--r--Kernel/Devices/FileBackedDiskDevice.cpp82
-rw-r--r--Kernel/Devices/FileBackedDiskDevice.h33
-rw-r--r--Kernel/Devices/FullDevice.cpp34
-rw-r--r--Kernel/Devices/FullDevice.h19
-rw-r--r--Kernel/Devices/IDEDiskDevice.cpp274
-rw-r--r--Kernel/Devices/IDEDiskDevice.h48
-rw-r--r--Kernel/Devices/KeyboardDevice.cpp228
-rw-r--r--Kernel/Devices/KeyboardDevice.h55
-rw-r--r--Kernel/Devices/NullDevice.cpp38
-rw-r--r--Kernel/Devices/NullDevice.h21
-rw-r--r--Kernel/Devices/PS2MouseDevice.cpp187
-rw-r--r--Kernel/Devices/PS2MouseDevice.h39
-rw-r--r--Kernel/Devices/RandomDevice.cpp58
-rw-r--r--Kernel/Devices/RandomDevice.h22
-rw-r--r--Kernel/Devices/ZeroDevice.cpp31
-rw-r--r--Kernel/Devices/ZeroDevice.h19
26 files changed, 1554 insertions, 0 deletions
diff --git a/Kernel/Devices/BXVGADevice.cpp b/Kernel/Devices/BXVGADevice.cpp
new file mode 100644
index 0000000000..8ff72b7fee
--- /dev/null
+++ b/Kernel/Devices/BXVGADevice.cpp
@@ -0,0 +1,144 @@
+#include <Kernel/Devices/BXVGADevice.h>
+#include <Kernel/IO.h>
+#include <Kernel/PCI.h>
+#include <Kernel/MemoryManager.h>
+#include <Kernel/Process.h>
+#include <LibC/errno_numbers.h>
+
+#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
+
+#define BXVGA_DEV_IOCTL_SET_Y_OFFSET 1982
+#define BXVGA_DEV_IOCTL_SET_RESOLUTION 1985
+struct BXVGAResolution {
+ int width;
+ int height;
+};
+
+static BXVGADevice* s_the;
+
+BXVGADevice& BXVGADevice::the()
+{
+ return *s_the;
+}
+
+BXVGADevice::BXVGADevice()
+ : BlockDevice(82, 413)
+{
+ s_the = this;
+ m_framebuffer_address = PhysicalAddress(find_framebuffer_address());
+}
+
+void BXVGADevice::set_register(word index, word data)
+{
+ IO::out16(VBE_DISPI_IOPORT_INDEX, index);
+ IO::out16(VBE_DISPI_IOPORT_DATA, data);
+}
+
+void BXVGADevice::set_resolution(int width, int height)
+{
+ m_framebuffer_size = { width, height };
+
+ set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
+ set_register(VBE_DISPI_INDEX_XRES, (word)width);
+ set_register(VBE_DISPI_INDEX_YRES, (word)height);
+ set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (word)width);
+ set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (word)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);
+}
+
+void BXVGADevice::set_y_offset(int offset)
+{
+ ASSERT(offset <= m_framebuffer_size.height());
+ set_register(VBE_DISPI_INDEX_Y_OFFSET, (word)offset);
+}
+
+dword 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 };
+ dword framebuffer_address = 0;
+ PCI::enumerate_all([&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;
+ kprintf("BXVGA: framebuffer @ P%x\n", framebuffer_address);
+ }
+ });
+ return framebuffer_address;
+}
+
+Region* BXVGADevice::mmap(Process& process, LinearAddress preferred_laddr, size_t offset, size_t size)
+{
+ ASSERT(offset == 0);
+ ASSERT(size == framebuffer_size_in_bytes());
+ auto vmo = VMObject::create_for_physical_range(framebuffer_address(), framebuffer_size_in_bytes());
+ auto* region = process.allocate_region_with_vmo(
+ preferred_laddr,
+ framebuffer_size_in_bytes(),
+ move(vmo),
+ 0,
+ "BXVGA Framebuffer",
+ true, true);
+ kprintf("BXVGA: %s(%u) created Region{%p} with size %u for framebuffer P%x with laddr L%x\n",
+ process.name().characters(), process.pid(),
+ region, region->size(), framebuffer_address().as_ptr(), region->laddr().get());
+ ASSERT(region);
+ return region;
+}
+
+int BXVGADevice::ioctl(Process& process, unsigned request, unsigned arg)
+{
+ switch (request) {
+ case BXVGA_DEV_IOCTL_SET_Y_OFFSET:
+ if (arg > (unsigned)m_framebuffer_size.height() * 2)
+ return -EINVAL;
+ set_y_offset((int)arg);
+ return 0;
+ case BXVGA_DEV_IOCTL_SET_RESOLUTION: {
+ auto* resolution = (const BXVGAResolution*)arg;
+ if (!process.validate_read_typed(resolution))
+ return -EFAULT;
+ set_resolution(resolution->width, resolution->height);
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ };
+}
+
+bool BXVGADevice::can_read(Process&) const
+{
+ ASSERT_NOT_REACHED();
+}
+
+bool BXVGADevice::can_write(Process&) const
+{
+ ASSERT_NOT_REACHED();
+}
+
+ssize_t BXVGADevice::read(Process&, byte*, ssize_t)
+{
+ ASSERT_NOT_REACHED();
+}
+
+ssize_t BXVGADevice::write(Process&, const byte*, ssize_t)
+{
+ ASSERT_NOT_REACHED();
+}
diff --git a/Kernel/Devices/BXVGADevice.h b/Kernel/Devices/BXVGADevice.h
new file mode 100644
index 0000000000..0025ac07a5
--- /dev/null
+++ b/Kernel/Devices/BXVGADevice.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <AK/Types.h>
+#include <AK/AKString.h>
+#include <SharedGraphics/Size.h>
+#include <Kernel/types.h>
+#include <Kernel/Devices/BlockDevice.h>
+
+class BXVGADevice final : public BlockDevice {
+ AK_MAKE_ETERNAL
+public:
+ static BXVGADevice& the();
+
+ BXVGADevice();
+
+ PhysicalAddress framebuffer_address() const { return m_framebuffer_address; }
+ void set_resolution(int width, int height);
+ void set_y_offset(int);
+
+ virtual int ioctl(Process&, unsigned request, unsigned arg) override;
+ virtual Region* mmap(Process&, LinearAddress preferred_laddr, size_t offset, size_t) override;
+
+ size_t framebuffer_size_in_bytes() const { return m_framebuffer_size.area() * sizeof(dword) * 2; }
+ Size framebuffer_size() const { return m_framebuffer_size; }
+
+private:
+ virtual const char* class_name() const override { return "BXVGA"; }
+ virtual bool can_read(Process&) const override;
+ virtual bool can_write(Process&) const override;
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+
+ void set_register(word index, word value);
+ dword find_framebuffer_address();
+
+ PhysicalAddress m_framebuffer_address;
+ Size m_framebuffer_size;
+};
diff --git a/Kernel/Devices/BlockDevice.cpp b/Kernel/Devices/BlockDevice.cpp
new file mode 100644
index 0000000000..d1911a18c9
--- /dev/null
+++ b/Kernel/Devices/BlockDevice.cpp
@@ -0,0 +1,5 @@
+#include <Kernel/Devices/BlockDevice.h>
+
+BlockDevice::~BlockDevice()
+{
+}
diff --git a/Kernel/Devices/BlockDevice.h b/Kernel/Devices/BlockDevice.h
new file mode 100644
index 0000000000..1161f7cbf8
--- /dev/null
+++ b/Kernel/Devices/BlockDevice.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <Kernel/Devices/Device.h>
+
+class BlockDevice : public Device {
+public:
+ virtual ~BlockDevice() override;
+
+ virtual Region* mmap(Process&, LinearAddress preferred_laddr, size_t offset, size_t size) = 0;
+
+protected:
+ BlockDevice(unsigned major, unsigned minor) : Device(major, minor) { }
+
+private:
+ virtual bool is_block_device() const final { return true; }
+};
diff --git a/Kernel/Devices/CharacterDevice.cpp b/Kernel/Devices/CharacterDevice.cpp
new file mode 100644
index 0000000000..44ce9eb7d3
--- /dev/null
+++ b/Kernel/Devices/CharacterDevice.cpp
@@ -0,0 +1,5 @@
+#include <Kernel/Devices/CharacterDevice.h>
+
+CharacterDevice::~CharacterDevice()
+{
+}
diff --git a/Kernel/Devices/CharacterDevice.h b/Kernel/Devices/CharacterDevice.h
new file mode 100644
index 0000000000..6dedf20743
--- /dev/null
+++ b/Kernel/Devices/CharacterDevice.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <Kernel/Devices/Device.h>
+
+class CharacterDevice : public Device {
+public:
+ virtual ~CharacterDevice() override;
+
+protected:
+ CharacterDevice(unsigned major, unsigned minor) : Device(major, minor) { }
+
+private:
+ virtual bool is_character_device() const final { return true; }
+};
diff --git a/Kernel/Devices/Device.cpp b/Kernel/Devices/Device.cpp
new file mode 100644
index 0000000000..e04f196e5b
--- /dev/null
+++ b/Kernel/Devices/Device.cpp
@@ -0,0 +1,28 @@
+#include "CharacterDevice.h"
+#include <LibC/errno_numbers.h>
+
+Device::Device(unsigned major, unsigned minor)
+ : m_major(major)
+ , m_minor(minor)
+{
+ VFS::the().register_device(*this);
+}
+
+Device::~Device()
+{
+ VFS::the().unregister_device(*this);
+}
+
+KResultOr<Retained<FileDescriptor>> Device::open(int options)
+{
+ return VFS::the().open(*this, options);
+}
+
+void Device::close()
+{
+}
+
+int Device::ioctl(Process&, unsigned, unsigned)
+{
+ return -ENOTTY;
+}
diff --git a/Kernel/Devices/Device.h b/Kernel/Devices/Device.h
new file mode 100644
index 0000000000..27e31bad8d
--- /dev/null
+++ b/Kernel/Devices/Device.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <AK/Retainable.h>
+#include <AK/Types.h>
+#include "Limits.h"
+#include "FileDescriptor.h"
+
+class Process;
+
+class Device : public Retainable<Device> {
+public:
+ virtual ~Device();
+
+ InodeMetadata metadata() const { return { }; }
+
+ virtual KResultOr<Retained<FileDescriptor>> open(int options);
+ virtual void close();
+
+ virtual bool can_read(Process&) const = 0;
+ virtual bool can_write(Process&) const = 0;
+
+ virtual ssize_t read(Process&, byte*, ssize_t) = 0;
+ virtual ssize_t write(Process&, const byte*, ssize_t) = 0;
+
+ unsigned major() const { return m_major; }
+ unsigned minor() const { return m_minor; }
+
+ virtual bool is_tty() const { return false; }
+ virtual bool is_master_pty() const { return false; }
+
+ virtual int ioctl(Process&, unsigned request, unsigned arg);
+
+ virtual const char* class_name() const = 0;
+
+ uid_t uid() const { return m_uid; }
+ uid_t gid() const { return m_gid; }
+
+ virtual bool is_block_device() const { return false; }
+ virtual bool is_character_device() const { return false; }
+
+protected:
+ Device(unsigned major, unsigned minor);
+ void set_uid(uid_t uid) { m_uid = uid; }
+ void set_gid(gid_t gid) { m_gid = gid; }
+
+private:
+ unsigned m_major { 0 };
+ unsigned m_minor { 0 };
+ uid_t m_uid { 0 };
+ gid_t m_gid { 0 };
+};
diff --git a/Kernel/Devices/DiskDevice.cpp b/Kernel/Devices/DiskDevice.cpp
new file mode 100644
index 0000000000..3c2b210641
--- /dev/null
+++ b/Kernel/Devices/DiskDevice.cpp
@@ -0,0 +1,42 @@
+#include <Kernel/Devices/DiskDevice.h>
+
+DiskDevice::DiskDevice()
+{
+}
+
+DiskDevice::~DiskDevice()
+{
+}
+
+bool DiskDevice::read(DiskOffset offset, unsigned length, byte* out) const
+{
+ ASSERT((offset % block_size()) == 0);
+ ASSERT((length % block_size()) == 0);
+ dword first_block = offset / block_size();
+ dword end_block = (offset + length) / block_size();
+ byte* outptr = out;
+ for (unsigned bi = first_block; bi < end_block; ++bi) {
+ if (!read_block(bi, outptr))
+ return false;
+ outptr += block_size();
+ }
+ return true;
+}
+
+bool DiskDevice::write(DiskOffset offset, unsigned length, const byte* in)
+{
+ ASSERT((offset % block_size()) == 0);
+ ASSERT((length % block_size()) == 0);
+ dword first_block = offset / block_size();
+ dword end_block = (offset + length) / block_size();
+ ASSERT(first_block <= 0xffffffff);
+ ASSERT(end_block <= 0xffffffff);
+ const byte* inptr = in;
+ for (unsigned bi = first_block; bi < end_block; ++bi) {
+ if (!write_block(bi, inptr))
+ return false;
+ inptr += block_size();
+ }
+ return true;
+}
+
diff --git a/Kernel/Devices/DiskDevice.h b/Kernel/Devices/DiskDevice.h
new file mode 100644
index 0000000000..071c5cd63b
--- /dev/null
+++ b/Kernel/Devices/DiskDevice.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <AK/Retainable.h>
+#include <AK/Types.h>
+
+// FIXME: Support 64-bit DiskOffset
+typedef dword DiskOffset;
+
+class DiskDevice : public Retainable<DiskDevice> {
+public:
+ virtual ~DiskDevice();
+
+ virtual unsigned block_size() const = 0;
+ virtual bool read_block(unsigned index, byte*) const = 0;
+ virtual bool write_block(unsigned index, const byte*) = 0;
+ virtual const char* class_name() const = 0;
+ bool read(DiskOffset, unsigned length, byte*) const;
+ bool write(DiskOffset, unsigned length, const byte*);
+
+protected:
+ DiskDevice();
+};
+
diff --git a/Kernel/Devices/FileBackedDiskDevice.cpp b/Kernel/Devices/FileBackedDiskDevice.cpp
new file mode 100644
index 0000000000..6e936fb778
--- /dev/null
+++ b/Kernel/Devices/FileBackedDiskDevice.cpp
@@ -0,0 +1,82 @@
+#define _FILE_OFFSET_BITS 64
+
+#include <Kernel/Devices/FileBackedDiskDevice.h>
+#include <cstring>
+#include <sys/stat.h>
+
+//#define FBBD_DEBUG
+#define IGNORE_FILE_LENGTH // Useful for e.g /dev/hda2
+
+RetainPtr<FileBackedDiskDevice> FileBackedDiskDevice::create(String&& image_path, unsigned block_size)
+{
+ return adopt(*new FileBackedDiskDevice(move(image_path), block_size));
+}
+
+FileBackedDiskDevice::FileBackedDiskDevice(String&& image_path, unsigned block_size)
+ : m_image_path(move(image_path))
+ , m_block_size(block_size)
+{
+ struct stat st;
+ int result = stat(m_image_path.characters(), &st);
+ ASSERT(result != -1);
+ m_file_length = st.st_size;
+ m_file = fopen(m_image_path.characters(), "r+");
+}
+
+FileBackedDiskDevice::~FileBackedDiskDevice()
+{
+}
+
+unsigned FileBackedDiskDevice::block_size() const
+{
+ return m_block_size;
+}
+
+bool FileBackedDiskDevice::read_block(unsigned index, byte* out) const
+{
+ DiskOffset offset = index * m_block_size;
+ return read_internal(offset, block_size(), out);
+}
+
+bool FileBackedDiskDevice::write_block(unsigned index, const byte* data)
+{
+ DiskOffset offset = index * m_block_size;
+ return write_internal(offset, block_size(), data);
+}
+
+bool FileBackedDiskDevice::read_internal(DiskOffset offset, unsigned length, byte* out) const
+{
+#ifndef IGNORE_FILE_LENGTH
+ if (offset + length >= m_file_length)
+ return false;
+#endif
+#ifdef FBBD_DEBUG
+ printf("[FileBackedDiskDevice] Read device @ offset %llx, length %u\n", offset, length);
+#endif
+ fseeko(m_file, offset, SEEK_SET);
+ unsigned nread = fread(out, sizeof(byte), length, m_file);
+ ASSERT(nread == length);
+ return true;
+}
+
+bool FileBackedDiskDevice::write_internal(DiskOffset offset, unsigned length, const byte* data)
+{
+#ifndef IGNORE_FILE_LENGTH
+ if (offset + length >= m_file_length)
+ return false;
+#endif
+#ifdef FBBD_DEBUG
+ printf("[FileBackedDiskDevice] Write device @ offset %llx, length %u\n", offset, length);
+#endif
+ fseeko(m_file, offset, SEEK_SET);
+ // size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+ unsigned nwritten = fwrite(data, sizeof(byte), length, m_file);
+ ASSERT(nwritten == length);
+ return true;
+}
+
+const char* FileBackedDiskDevice::class_name() const
+{
+ return "FileBackedDiskDevice";
+}
+
diff --git a/Kernel/Devices/FileBackedDiskDevice.h b/Kernel/Devices/FileBackedDiskDevice.h
new file mode 100644
index 0000000000..c16c848f8b
--- /dev/null
+++ b/Kernel/Devices/FileBackedDiskDevice.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <Kernel/Devices/DiskDevice.h>
+#include <AK/RetainPtr.h>
+#include <AK/AKString.h>
+#include <AK/Types.h>
+#include <stdio.h>
+
+class FileBackedDiskDevice final : public DiskDevice {
+public:
+ static RetainPtr<FileBackedDiskDevice> create(String&& image_path, unsigned block_size);
+ virtual ~FileBackedDiskDevice() override;
+
+ bool is_valid() const { return m_file; }
+
+ virtual unsigned block_size() const override;
+ virtual bool read_block(unsigned index, byte* out) const override;
+ virtual bool write_block(unsigned index, const byte*) override;
+
+private:
+ virtual const char* class_name() const override;
+
+ bool read_internal(DiskOffset, unsigned length, byte* out) const;
+ bool write_internal(DiskOffset, unsigned length, const byte* data);
+
+ FileBackedDiskDevice(String&& imagePath, unsigned block_size);
+
+ String m_image_path;
+ FILE* m_file { nullptr };
+ DiskOffset m_file_length { 0 };
+ unsigned m_block_size { 0 };
+};
+
diff --git a/Kernel/Devices/FullDevice.cpp b/Kernel/Devices/FullDevice.cpp
new file mode 100644
index 0000000000..6d7acfadc7
--- /dev/null
+++ b/Kernel/Devices/FullDevice.cpp
@@ -0,0 +1,34 @@
+#include "FullDevice.h"
+#include "Limits.h"
+#include <LibC/errno_numbers.h>
+#include <AK/StdLibExtras.h>
+#include <AK/kstdio.h>
+
+FullDevice::FullDevice()
+ : CharacterDevice(1, 7)
+{
+}
+
+FullDevice::~FullDevice()
+{
+}
+
+bool FullDevice::can_read(Process&) const
+{
+ return true;
+}
+
+ssize_t FullDevice::read(Process&, byte* buffer, ssize_t size)
+{
+ ssize_t count = min(GoodBufferSize, size);
+ memset(buffer, 0, (size_t)count);
+ return count;
+}
+
+ssize_t FullDevice::write(Process&, const byte*, ssize_t size)
+{
+ if (size == 0)
+ return 0;
+ return -ENOSPC;
+}
+
diff --git a/Kernel/Devices/FullDevice.h b/Kernel/Devices/FullDevice.h
new file mode 100644
index 0000000000..b494656047
--- /dev/null
+++ b/Kernel/Devices/FullDevice.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "CharacterDevice.h"
+
+class FullDevice final : public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ FullDevice();
+ virtual ~FullDevice() override;
+
+private:
+ // ^CharacterDevice
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+ virtual bool can_read(Process&) const override;
+ virtual bool can_write(Process&) const override { return true; }
+ virtual const char* class_name() const override { return "FullDevice"; }
+};
+
diff --git a/Kernel/Devices/IDEDiskDevice.cpp b/Kernel/Devices/IDEDiskDevice.cpp
new file mode 100644
index 0000000000..1b0eb27061
--- /dev/null
+++ b/Kernel/Devices/IDEDiskDevice.cpp
@@ -0,0 +1,274 @@
+#include "IDEDiskDevice.h"
+#include "types.h"
+#include "Process.h"
+#include "StdLib.h"
+#include "IO.h"
+#include "Scheduler.h"
+#include "PIC.h"
+#include <Kernel/Lock.h>
+
+//#define DISK_DEBUG
+
+#define IRQ_FIXED_DISK 14
+
+#define IDE0_DATA 0x1F0
+#define IDE0_STATUS 0x1F7
+#define IDE0_COMMAND 0x1F7
+
+enum IDECommand : byte {
+ IDENTIFY_DRIVE = 0xEC,
+ READ_SECTORS = 0x21,
+ WRITE_SECTORS = 0x30,
+};
+
+enum IDEStatus : byte {
+ BUSY = (1 << 7),
+ DRDY = (1 << 6),
+ DF = (1 << 5),
+ SRV = (1 << 4),
+ DRQ = (1 << 3),
+ CORR = (1 << 2),
+ IDX = (1 << 1),
+ ERR = (1 << 0),
+};
+
+Retained<IDEDiskDevice> IDEDiskDevice::create()
+{
+ return adopt(*new IDEDiskDevice);
+}
+
+IDEDiskDevice::IDEDiskDevice()
+ : IRQHandler(IRQ_FIXED_DISK)
+ , m_lock("IDEDiskDevice")
+{
+ initialize();
+}
+
+IDEDiskDevice::~IDEDiskDevice()
+{
+}
+
+const char* IDEDiskDevice::class_name() const
+{
+ return "IDEDiskDevice";
+}
+
+unsigned IDEDiskDevice::block_size() const
+{
+ return 512;
+}
+
+bool IDEDiskDevice::read_block(unsigned index, byte* out) const
+{
+ return const_cast<IDEDiskDevice&>(*this).read_sectors(index, 1, out);
+}
+
+bool IDEDiskDevice::write_block(unsigned index, const byte* data)
+{
+ return write_sectors(index, 1, data);
+}
+
+static void print_ide_status(byte status)
+{
+ kprintf("DRQ=%u BUSY=%u DRDY=%u SRV=%u DF=%u CORR=%u IDX=%u ERR=%u\n",
+ (status & DRQ) != 0,
+ (status & BUSY) != 0,
+ (status & DRDY) != 0,
+ (status & SRV) != 0,
+ (status & DF) != 0,
+ (status & CORR) != 0,
+ (status & IDX) != 0,
+ (status & ERR) != 0);
+}
+
+bool IDEDiskDevice::wait_for_irq()
+{
+#ifdef DISK_DEBUG
+ kprintf("disk: waiting for interrupt...\n");
+#endif
+ // FIXME: Add timeout.
+ while (!m_interrupted) {
+ // FIXME: Put this process into a Blocked state instead, it's stupid to wake up just to check a flag.
+ Scheduler::yield();
+ }
+#ifdef DISK_DEBUG
+ kprintf("disk: got interrupt!\n");
+#endif
+ memory_barrier();
+ return true;
+}
+
+void IDEDiskDevice::handle_irq()
+{
+ byte status = IO::in8(0x1f7);
+ if (status & ERR) {
+ print_ide_status(status);
+ m_device_error = IO::in8(0x1f1);
+ kprintf("IDEDiskDevice: Error %b!\n", m_device_error);
+ } else {
+ m_device_error = 0;
+ }
+#ifdef DISK_DEBUG
+ kprintf("disk:interrupt: DRQ=%u BUSY=%u DRDY=%u\n", (status & DRQ) != 0, (status & BUSY) != 0, (status & DRDY) != 0);
+#endif
+ m_interrupted = true;
+}
+
+void IDEDiskDevice::initialize()
+{
+#ifdef DISK_DEBUG
+ byte status = IO::in8(IDE0_STATUS);
+ kprintf("initial status: ");
+ print_ide_status(status);
+#endif
+
+ m_interrupted = false;
+
+ while (IO::in8(IDE0_STATUS) & BUSY);
+
+ enable_irq();
+
+ IO::out8(0x1F6, 0xA0); // 0xB0 for 2nd device
+ IO::out8(0x3F6, 0xA0); // 0xB0 for 2nd device
+ IO::out8(IDE0_COMMAND, IDENTIFY_DRIVE);
+
+ enable_irq();
+ wait_for_irq();
+
+ ByteBuffer wbuf = ByteBuffer::create_uninitialized(512);
+ ByteBuffer bbuf = ByteBuffer::create_uninitialized(512);
+ byte* b = bbuf.pointer();
+ word* w = (word*)wbuf.pointer();
+ const word* wbufbase = (word*)wbuf.pointer();
+
+ for (dword i = 0; i < 256; ++i) {
+ word data = IO::in16(IDE0_DATA);
+ *(w++) = data;
+ *(b++) = MSB(data);
+ *(b++) = LSB(data);
+ }
+
+ // "Unpad" the device name string.
+ for (dword i = 93; i > 54 && bbuf[i] == ' '; --i)
+ bbuf[i] = 0;
+
+ m_cylinders = wbufbase[1];
+ m_heads = wbufbase[3];
+ m_sectors_per_track = wbufbase[6];
+
+ kprintf(
+ "IDEDiskDevice: Master=\"%s\", C/H/Spt=%u/%u/%u\n",
+ bbuf.pointer() + 54,
+ m_cylinders,
+ m_heads,
+ m_sectors_per_track
+ );
+}
+
+IDEDiskDevice::CHS IDEDiskDevice::lba_to_chs(dword lba) const
+{
+ CHS chs;
+ chs.cylinder = lba / (m_sectors_per_track * m_heads);
+ chs.head = (lba / m_sectors_per_track) % m_heads;
+ chs.sector = (lba % m_sectors_per_track) + 1;
+ return chs;
+}
+
+bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf)
+{
+ LOCKER(m_lock);
+#ifdef DISK_DEBUG
+ dbgprintf("%s: Disk::read_sectors request (%u sector(s) @ %u)\n",
+ current->process().name().characters(),
+ count,
+ start_sector);
+#endif
+ disable_irq();
+
+ auto chs = lba_to_chs(start_sector);
+
+ while (IO::in8(IDE0_STATUS) & BUSY);
+
+#ifdef DISK_DEBUG
+ kprintf("IDEDiskDevice: Reading %u sector(s) @ LBA %u (%u/%u/%u)\n", count, start_sector, chs.cylinder, chs.head, chs.sector);
+#endif
+
+ IO::out8(0x1F2, count == 256 ? 0 : LSB(count));
+ IO::out8(0x1F3, chs.sector);
+ IO::out8(0x1F4, LSB(chs.cylinder));
+ IO::out8(0x1F5, MSB(chs.cylinder));
+
+ IO::out8(0x1F6, 0xA0 | chs.head); /* 0xB0 for 2nd device */
+
+ IO::out8(0x3F6, 0x08);
+ while (!(IO::in8(IDE0_STATUS) & DRDY));
+
+ IO::out8(IDE0_COMMAND, READ_SECTORS);
+ m_interrupted = false;
+ enable_irq();
+ wait_for_irq();
+
+ if (m_device_error)
+ return false;
+
+ byte status = IO::in8(0x1f7);
+ if (status & DRQ) {
+#ifdef DISK_DEBUG
+ kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf);
+#endif
+ for (dword i = 0; i < (count * 512); i += 2) {
+ word w = IO::in16(IDE0_DATA);
+ outbuf[i] = LSB(w);
+ outbuf[i+1] = MSB(w);
+ }
+ }
+
+ return true;
+}
+
+bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* data)
+{
+ LOCKER(m_lock);
+#ifdef DISK_DEBUG
+ dbgprintf("%s(%u): IDEDiskDevice::write_sectors request (%u sector(s) @ %u)\n",
+ current->process().name().characters(),
+ current->pid(),
+ count,
+ start_sector);
+#endif
+ disable_irq();
+
+ auto chs = lba_to_chs(start_sector);
+
+ while (IO::in8(IDE0_STATUS) & BUSY);
+
+ //dbgprintf("IDEDiskDevice: Writing %u sector(s) @ LBA %u (%u/%u/%u)\n", count, start_sector, chs.cylinder, chs.head, chs.sector);
+
+ IO::out8(0x1F2, count == 256 ? 0 : LSB(count));
+ IO::out8(0x1F3, chs.sector);
+ IO::out8(0x1F4, LSB(chs.cylinder));
+ IO::out8(0x1F5, MSB(chs.cylinder));
+
+ IO::out8(0x1F6, 0xA0 | chs.head); /* 0xB0 for 2nd device */
+
+ IO::out8(0x3F6, 0x08);
+
+ IO::out8(IDE0_COMMAND, WRITE_SECTORS);
+
+ while (!(IO::in8(IDE0_STATUS) & DRQ));
+
+ byte status = IO::in8(0x1f7);
+ if (status & DRQ) {
+ //dbgprintf("Sending %u bytes (status=%b), data=%p...\n", count * 512, status, data);
+ auto* data_as_words = (const word*)data;
+ for (dword i = 0; i < (count * 512) / 2; ++i) {
+ IO::out16(IDE0_DATA, data_as_words[i]);
+ }
+ }
+
+ m_interrupted = false;
+ enable_irq();
+ wait_for_irq();
+
+ return !m_device_error;
+}
diff --git a/Kernel/Devices/IDEDiskDevice.h b/Kernel/Devices/IDEDiskDevice.h
new file mode 100644
index 0000000000..250889f964
--- /dev/null
+++ b/Kernel/Devices/IDEDiskDevice.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <Kernel/Lock.h>
+#include <AK/RetainPtr.h>
+#include <Kernel/Devices/DiskDevice.h>
+#include "IRQHandler.h"
+
+class IDEDiskDevice final : public IRQHandler, public DiskDevice {
+public:
+ static Retained<IDEDiskDevice> create();
+ virtual ~IDEDiskDevice() override;
+
+ // ^DiskDevice
+ virtual unsigned block_size() const override;
+ virtual bool read_block(unsigned index, byte*) const override;
+ virtual bool write_block(unsigned index, const byte*) override;
+
+protected:
+ IDEDiskDevice();
+
+private:
+ // ^IRQHandler
+ virtual void handle_irq() override;
+
+ // ^DiskDevice
+ virtual const char* class_name() const override;
+
+ struct CHS {
+ dword cylinder;
+ word head;
+ word sector;
+ };
+ CHS lba_to_chs(dword) const;
+
+ void initialize();
+ bool wait_for_irq();
+ bool read_sectors(dword start_sector, word count, byte* buffer);
+ bool write_sectors(dword start_sector, word count, const byte* data);
+
+ Lock m_lock;
+ word m_cylinders { 0 };
+ word m_heads { 0 };
+ word m_sectors_per_track { 0 };
+ volatile bool m_interrupted { false };
+ volatile byte m_device_error { 0 };
+
+};
+
diff --git a/Kernel/Devices/KeyboardDevice.cpp b/Kernel/Devices/KeyboardDevice.cpp
new file mode 100644
index 0000000000..e5b57b7f84
--- /dev/null
+++ b/Kernel/Devices/KeyboardDevice.cpp
@@ -0,0 +1,228 @@
+#include "types.h"
+#include "i386.h"
+#include "IO.h"
+#include "PIC.h"
+#include <Kernel/Devices/KeyboardDevice.h>
+#include <Kernel/TTY/VirtualConsole.h>
+#include <AK/Assertions.h>
+
+//#define KEYBOARD_DEBUG
+
+#define IRQ_KEYBOARD 1
+#define I8042_BUFFER 0x60
+#define I8042_STATUS 0x64
+#define I8042_ACK 0xFA
+#define I8042_BUFFER_FULL 0x01
+#define I8042_WHICH_BUFFER 0x20
+#define I8042_MOUSE_BUFFER 0x20
+#define I8042_KEYBOARD_BUFFER 0x00
+
+static char map[0x80] =
+{
+ 0, '\033', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, '\t',
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0,
+ 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\',
+ 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
+ 0, 0, 0, ' '
+};
+
+static char shift_map[0x80] =
+{
+ 0, '\033', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0x08, '\t',
+ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n', 0,
+ 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|',
+ 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
+ 0, 0, 0, ' '
+};
+
+static KeyCode unshifted_key_map[0x80] =
+{
+ Key_Invalid, Key_Escape,
+ Key_1, Key_2, Key_3, Key_4, Key_5, Key_6, Key_7, Key_8, Key_9, Key_0, Key_Minus, Key_Equal, Key_Backspace,
+ Key_Tab, //15
+ Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_LeftBracket, Key_RightBracket,
+ Key_Return, // 28
+ Key_Control, // 29
+ Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Apostrophe, Key_Backtick,
+ Key_Shift, // 42
+ Key_Backslash,
+ Key_Z, Key_X, Key_C, Key_V, Key_B, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash,
+ Key_Alt, // 54
+ Key_Invalid, Key_Invalid,
+ Key_Space, // 57
+ Key_Invalid, // 58
+ Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10,
+ Key_Invalid,
+ Key_Invalid, // 70
+ Key_Home,
+ Key_Up,
+ Key_PageUp,
+ Key_Invalid,
+ Key_Left,
+ Key_Invalid,
+ Key_Right, // 77
+ Key_Invalid,
+ Key_End,
+ Key_Down, // 80
+ Key_PageDown,
+ Key_Invalid,
+ Key_Delete, // 83
+ Key_Invalid,
+ Key_Invalid,
+ Key_Invalid,
+ Key_F11,
+ Key_F12,
+ Key_Invalid,
+ Key_Invalid,
+ Key_Logo,
+};
+
+static KeyCode shifted_key_map[0x100] =
+{
+ Key_Invalid, Key_Escape,
+ Key_ExclamationPoint, Key_AtSign, Key_Hashtag, Key_Dollar, Key_Percent, Key_Circumflex, Key_Ampersand, Key_Asterisk, Key_LeftParen, Key_RightParen, Key_Underscore, Key_Plus, Key_Backspace,
+ Key_Tab,
+ Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_LeftBrace, Key_RightBrace,
+ Key_Return,
+ Key_Control,
+ Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Colon, Key_DoubleQuote, Key_Tilde,
+ Key_Shift,
+ Key_Pipe,
+ Key_Z, Key_X, Key_C, Key_V, Key_B, Key_N, Key_M, Key_LessThan, Key_GreaterThan, Key_QuestionMark,
+ Key_Alt,
+ Key_Invalid, Key_Invalid,
+ Key_Space, // 57
+ Key_Invalid, // 58
+ Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10,
+ Key_Invalid,
+ Key_Invalid, // 70
+ Key_Home,
+ Key_Up,
+ Key_PageUp,
+ Key_Invalid,
+ Key_Left,
+ Key_Invalid,
+ Key_Right, // 77
+ Key_Invalid,
+ Key_End,
+ Key_Down, // 80
+ Key_PageDown,
+ Key_Invalid,
+ Key_Delete, // 83
+ Key_Invalid,
+ Key_Invalid,
+ Key_Invalid,
+ Key_F11,
+ Key_F12,
+ Key_Invalid,
+ Key_Invalid,
+ Key_Logo,
+};
+
+void KeyboardDevice::key_state_changed(byte raw, bool pressed)
+{
+ Event event;
+ event.key = (m_modifiers & Mod_Shift) ? shifted_key_map[raw] : unshifted_key_map[raw];
+ event.character = (m_modifiers & Mod_Shift) ? shift_map[raw] : map[raw];
+ event.flags = m_modifiers;
+ if (pressed)
+ event.flags |= Is_Press;
+ if (m_client)
+ m_client->on_key_pressed(event);
+ m_queue.enqueue(event);
+}
+
+void KeyboardDevice::handle_irq()
+{
+ for (;;) {
+ byte status = IO::in8(I8042_STATUS);
+ if (!(((status & I8042_WHICH_BUFFER) == I8042_KEYBOARD_BUFFER) && (status & I8042_BUFFER_FULL)))
+ return;
+ byte raw = IO::in8(I8042_BUFFER);
+ byte ch = raw & 0x7f;
+ bool pressed = !(raw & 0x80);
+
+#ifdef KEYBOARD_DEBUG
+ dbgprintf("Keyboard::handle_irq: %b %s\n", ch, pressed ? "down" : "up");
+#endif
+ switch (ch) {
+ case 0x38: update_modifier(Mod_Alt, pressed); break;
+ case 0x1d: update_modifier(Mod_Ctrl, pressed); break;
+ case 0x2a: update_modifier(Mod_Shift, pressed); break;
+ case 0x5b: update_modifier(Mod_Logo, pressed); break;
+ }
+ switch (ch) {
+ case I8042_ACK: break;
+ default:
+ if (m_modifiers & Mod_Alt) {
+ switch (map[ch]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ VirtualConsole::switch_to(map[ch] - '0' - 1);
+ break;
+ default:
+ break;
+ }
+ }
+ key_state_changed(ch, pressed);
+ }
+ }
+}
+
+static KeyboardDevice* s_the;
+
+KeyboardDevice& KeyboardDevice::the()
+{
+ ASSERT(s_the);
+ return *s_the;
+}
+
+KeyboardDevice::KeyboardDevice()
+ : IRQHandler(IRQ_KEYBOARD)
+ , CharacterDevice(85, 1)
+{
+ s_the = this;
+
+ // Empty the buffer of any pending data.
+ // I don't care what you've been pressing until now!
+ while (IO::in8(I8042_STATUS) & I8042_BUFFER_FULL)
+ IO::in8(I8042_BUFFER);
+
+ enable_irq();
+}
+
+KeyboardDevice::~KeyboardDevice()
+{
+}
+
+bool KeyboardDevice::can_read(Process&) const
+{
+ return !m_queue.is_empty();
+}
+
+ssize_t KeyboardDevice::read(Process&, byte* buffer, ssize_t size)
+{
+ ssize_t nread = 0;
+ while (nread < size) {
+ if (m_queue.is_empty())
+ break;
+ // Don't return partial data frames.
+ if ((size - nread) < (ssize_t)sizeof(Event))
+ break;
+ auto event = m_queue.dequeue();
+ memcpy(buffer, &event, sizeof(Event));
+ nread += sizeof(Event);
+ }
+ return nread;
+}
+
+ssize_t KeyboardDevice::write(Process&, const byte*, ssize_t)
+{
+ return 0;
+}
+
+KeyboardClient::~KeyboardClient()
+{
+}
diff --git a/Kernel/Devices/KeyboardDevice.h b/Kernel/Devices/KeyboardDevice.h
new file mode 100644
index 0000000000..8931deab12
--- /dev/null
+++ b/Kernel/Devices/KeyboardDevice.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <AK/Types.h>
+#include <AK/DoublyLinkedList.h>
+#include <AK/CircularQueue.h>
+#include <Kernel/Devices/CharacterDevice.h>
+#include "IRQHandler.h"
+#include "KeyCode.h"
+
+class KeyboardClient;
+
+class KeyboardDevice final : public IRQHandler, public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ using Event = KeyEvent;
+
+ [[gnu::pure]] static KeyboardDevice& the();
+
+ virtual ~KeyboardDevice() override;
+ KeyboardDevice();
+
+ void set_client(KeyboardClient* client) { m_client = client; }
+
+ // ^CharacterDevice
+ virtual ssize_t read(Process&, byte* buffer, ssize_t) override;
+ virtual bool can_read(Process&) const override;
+ virtual ssize_t write(Process&, const byte* buffer, ssize_t) override;
+ virtual bool can_write(Process&) const override { return true; }
+
+private:
+ // ^IRQHandler
+ virtual void handle_irq() override;
+
+ // ^CharacterDevice
+ virtual const char* class_name() const override { return "KeyboardDevice"; }
+
+ void key_state_changed(byte raw, bool pressed);
+ void update_modifier(byte modifier, bool state)
+ {
+ if (state)
+ m_modifiers |= modifier;
+ else
+ m_modifiers &= ~modifier;
+ }
+
+ KeyboardClient* m_client { nullptr };
+ CircularQueue<Event, 16> m_queue;
+ byte m_modifiers { 0 };
+};
+
+class KeyboardClient {
+public:
+ virtual ~KeyboardClient();
+ virtual void on_key_pressed(KeyboardDevice::Event) = 0;
+};
diff --git a/Kernel/Devices/NullDevice.cpp b/Kernel/Devices/NullDevice.cpp
new file mode 100644
index 0000000000..cfb73450e1
--- /dev/null
+++ b/Kernel/Devices/NullDevice.cpp
@@ -0,0 +1,38 @@
+#include "NullDevice.h"
+#include "Limits.h"
+#include <AK/StdLibExtras.h>
+#include <AK/kstdio.h>
+
+static NullDevice* s_the;
+
+NullDevice& NullDevice::the()
+{
+ ASSERT(s_the);
+ return *s_the;
+}
+
+NullDevice::NullDevice()
+ : CharacterDevice(1, 3)
+{
+ s_the = this;
+}
+
+NullDevice::~NullDevice()
+{
+}
+
+bool NullDevice::can_read(Process&) const
+{
+ return true;
+}
+
+ssize_t NullDevice::read(Process&, byte*, ssize_t)
+{
+ return 0;
+}
+
+ssize_t NullDevice::write(Process&, const byte*, ssize_t buffer_size)
+{
+ return min(GoodBufferSize, buffer_size);
+}
+
diff --git a/Kernel/Devices/NullDevice.h b/Kernel/Devices/NullDevice.h
new file mode 100644
index 0000000000..d325bbecb5
--- /dev/null
+++ b/Kernel/Devices/NullDevice.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "CharacterDevice.h"
+
+class NullDevice final : public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ NullDevice();
+ virtual ~NullDevice() override;
+
+ static NullDevice& the();
+
+private:
+ // ^CharacterDevice
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+ virtual bool can_write(Process&) const override { return true; }
+ virtual bool can_read(Process&) const override;
+ virtual const char* class_name() const override { return "NullDevice"; }
+};
+
diff --git a/Kernel/Devices/PS2MouseDevice.cpp b/Kernel/Devices/PS2MouseDevice.cpp
new file mode 100644
index 0000000000..b21f489b63
--- /dev/null
+++ b/Kernel/Devices/PS2MouseDevice.cpp
@@ -0,0 +1,187 @@
+#include "PS2MouseDevice.h"
+#include "IO.h"
+
+#define IRQ_MOUSE 1
+#define I8042_BUFFER 0x60
+#define I8042_STATUS 0x64
+#define I8042_ACK 0xFA
+#define I8042_BUFFER_FULL 0x01
+#define I8042_WHICH_BUFFER 0x20
+#define I8042_MOUSE_BUFFER 0x20
+#define I8042_KEYBOARD_BUFFER 0x00
+
+//#define PS2MOUSE_DEBUG
+
+static PS2MouseDevice* s_the;
+
+PS2MouseDevice::PS2MouseDevice()
+ : IRQHandler(12)
+ , CharacterDevice(10, 1)
+{
+ s_the = this;
+ initialize();
+}
+
+PS2MouseDevice::~PS2MouseDevice()
+{
+}
+
+PS2MouseDevice& PS2MouseDevice::the()
+{
+ return *s_the;
+}
+
+void PS2MouseDevice::handle_irq()
+{
+ for (;;) {
+ byte status = IO::in8(I8042_STATUS);
+ if (!(((status & I8042_WHICH_BUFFER) == I8042_MOUSE_BUFFER) && (status & I8042_BUFFER_FULL)))
+ return;
+
+ byte data = IO::in8(I8042_BUFFER);
+ m_data[m_data_state] = data;
+ switch (m_data_state) {
+ case 0:
+ if (!(data & 0x08)) {
+ dbgprintf("PS2Mouse: Stream out of sync.\n");
+ break;
+ }
+ ++m_data_state;
+ break;
+ case 1:
+ ++m_data_state;
+ break;
+ case 2:
+ m_data_state = 0;
+#ifdef PS2MOUSE_DEBUG
+ dbgprintf("PS2Mouse: %d, %d %s %s (buffered: %u)\n",
+ m_data[1],
+ m_data[2],
+ (m_data[0] & 1) ? "Left" : "",
+ (m_data[0] & 2) ? "Right" : "",
+ m_queue.size()
+ );
+#endif
+ parse_data_packet();
+ break;
+ }
+ }
+}
+
+void PS2MouseDevice::parse_data_packet()
+{
+ int x = m_data[1];
+ int y = m_data[2];
+ bool x_overflow = m_data[0] & 0x40;
+ bool y_overflow = m_data[0] & 0x80;
+ bool x_sign = m_data[0] & 0x10;
+ bool y_sign = m_data[0] & 0x20;
+ if (x && x_sign)
+ x -= 0x100;
+ if (y && y_sign)
+ y -= 0x100;
+ if (x_overflow || y_overflow) {
+ x = 0;
+ y = 0;
+ }
+ MousePacket packet;
+ packet.dx = x;
+ packet.dy = y;
+ packet.buttons = m_data[0] & 0x07;
+ m_queue.enqueue(packet);
+}
+
+void PS2MouseDevice::wait_then_write(byte port, byte data)
+{
+ prepare_for_output();
+ IO::out8(port, data);
+}
+
+byte PS2MouseDevice::wait_then_read(byte port)
+{
+ prepare_for_input();
+ return IO::in8(port);
+}
+
+void PS2MouseDevice::initialize()
+{
+ // Enable PS aux port
+ wait_then_write(0x64, 0xa8);
+
+ // Enable interrupts
+ wait_then_write(0x64, 0x20);
+
+ // Enable the PS/2 mouse IRQ (12).
+ // NOTE: The keyboard uses IRQ 1 (and is enabled by bit 0 in this register).
+ byte status = wait_then_read(0x60) | 2;
+ wait_then_write(0x64, 0x60);
+ wait_then_write(0x60, status);
+
+ // Set default settings.
+ mouse_write(0xf6);
+ byte ack1 = mouse_read();
+ ASSERT(ack1 == 0xfa);
+
+ // Enable.
+ mouse_write(0xf4);
+ byte ack2 = mouse_read();
+ ASSERT(ack2 == 0xfa);
+
+ enable_irq();
+}
+
+void PS2MouseDevice::prepare_for_input()
+{
+ for (;;) {
+ if (IO::in8(0x64) & 1)
+ return;
+ }
+}
+
+void PS2MouseDevice::prepare_for_output()
+{
+ for (;;) {
+ if (!(IO::in8(0x64) & 2))
+ return;
+ }
+}
+
+void PS2MouseDevice::mouse_write(byte data)
+{
+ prepare_for_output();
+ IO::out8(0x64, 0xd4);
+ prepare_for_output();
+ IO::out8(0x60, data);
+}
+
+byte PS2MouseDevice::mouse_read()
+{
+ prepare_for_input();
+ return IO::in8(0x60);
+}
+
+bool PS2MouseDevice::can_read(Process&) const
+{
+ return !m_queue.is_empty();
+}
+
+ssize_t PS2MouseDevice::read(Process&, byte* buffer, ssize_t size)
+{
+ ssize_t nread = 0;
+ while (nread < size) {
+ if (m_queue.is_empty())
+ break;
+ // Don't return partial data frames.
+ if ((size - nread) < (ssize_t)sizeof(MousePacket))
+ break;
+ auto packet = m_queue.dequeue();
+ memcpy(buffer, &packet, sizeof(MousePacket));
+ nread += sizeof(MousePacket);
+ }
+ return nread;
+}
+
+ssize_t PS2MouseDevice::write(Process&, const byte*, ssize_t)
+{
+ return 0;
+}
diff --git a/Kernel/Devices/PS2MouseDevice.h b/Kernel/Devices/PS2MouseDevice.h
new file mode 100644
index 0000000000..2410a344f3
--- /dev/null
+++ b/Kernel/Devices/PS2MouseDevice.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <Kernel/Devices/CharacterDevice.h>
+#include <Kernel/MousePacket.h>
+#include <Kernel/IRQHandler.h>
+
+class PS2MouseDevice final : public IRQHandler, public CharacterDevice {
+public:
+ PS2MouseDevice();
+ virtual ~PS2MouseDevice() override;
+
+ static PS2MouseDevice& the();
+
+ // ^CharacterDevice
+ virtual bool can_read(Process&) const override;
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+ virtual bool can_write(Process&) const override { return true; }
+
+private:
+ // ^IRQHandler
+ virtual void handle_irq() override;
+
+ // ^CharacterDevice
+ virtual const char* class_name() const override { return "PS2MouseDevice"; }
+
+ void initialize();
+ void prepare_for_input();
+ void prepare_for_output();
+ void mouse_write(byte);
+ byte mouse_read();
+ void wait_then_write(byte port, byte data);
+ byte wait_then_read(byte port);
+ void parse_data_packet();
+
+ CircularQueue<MousePacket, 100> m_queue;
+ byte m_data_state { 0 };
+ byte m_data[3];
+};
diff --git a/Kernel/Devices/RandomDevice.cpp b/Kernel/Devices/RandomDevice.cpp
new file mode 100644
index 0000000000..7f4face5cb
--- /dev/null
+++ b/Kernel/Devices/RandomDevice.cpp
@@ -0,0 +1,58 @@
+#include "RandomDevice.h"
+#include "Limits.h"
+#include <AK/StdLibExtras.h>
+
+RandomDevice::RandomDevice()
+ : CharacterDevice(1, 8)
+{
+}
+
+RandomDevice::~RandomDevice()
+{
+}
+
+// Simple rand() and srand() borrowed from the POSIX standard:
+
+static unsigned long next = 1;
+
+#define MY_RAND_MAX 32767
+int RandomDevice::random_value()
+{
+ next = next * 1103515245 + 12345;
+ return((unsigned)(next/((MY_RAND_MAX + 1) * 2)) % (MY_RAND_MAX + 1));
+}
+
+float RandomDevice::random_percentage()
+{
+ return (float)random_value() / (float)MY_RAND_MAX;
+}
+
+#if 0
+static void mysrand(unsigned seed)
+{
+ next = seed;
+}
+#endif
+
+bool RandomDevice::can_read(Process&) const
+{
+ return true;
+}
+
+ssize_t RandomDevice::read(Process&, byte* buffer, ssize_t size)
+{
+ const int range = 'z' - 'a';
+ ssize_t nread = min(size, GoodBufferSize);
+ for (ssize_t i = 0; i < nread; ++i) {
+ dword r = random_value() % range;
+ buffer[i] = (byte)('a' + r);
+ }
+ return nread;
+}
+
+ssize_t RandomDevice::write(Process&, const byte*, ssize_t size)
+{
+ // FIXME: Use input for entropy? I guess that could be a neat feature?
+ return min(GoodBufferSize, size);
+}
+
diff --git a/Kernel/Devices/RandomDevice.h b/Kernel/Devices/RandomDevice.h
new file mode 100644
index 0000000000..9983d1fdc3
--- /dev/null
+++ b/Kernel/Devices/RandomDevice.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "CharacterDevice.h"
+
+class RandomDevice final : public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ RandomDevice();
+ virtual ~RandomDevice() override;
+
+ static int random_value();
+ static float random_percentage();
+
+private:
+ // ^CharacterDevice
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+ virtual bool can_read(Process&) const override;
+ virtual bool can_write(Process&) const override { return true; }
+ virtual const char* class_name() const override { return "RandomDevice"; }
+};
+
diff --git a/Kernel/Devices/ZeroDevice.cpp b/Kernel/Devices/ZeroDevice.cpp
new file mode 100644
index 0000000000..3527e85b55
--- /dev/null
+++ b/Kernel/Devices/ZeroDevice.cpp
@@ -0,0 +1,31 @@
+#include "ZeroDevice.h"
+#include "Limits.h"
+#include <AK/StdLibExtras.h>
+#include <AK/kstdio.h>
+
+ZeroDevice::ZeroDevice()
+ : CharacterDevice(1, 5)
+{
+}
+
+ZeroDevice::~ZeroDevice()
+{
+}
+
+bool ZeroDevice::can_read(Process&) const
+{
+ return true;
+}
+
+ssize_t ZeroDevice::read(Process&, byte* buffer, ssize_t size)
+{
+ ssize_t count = min(GoodBufferSize, size);
+ memset(buffer, 0, (size_t)count);
+ return count;
+}
+
+ssize_t ZeroDevice::write(Process&, const byte*, ssize_t size)
+{
+ return min(GoodBufferSize, size);
+}
+
diff --git a/Kernel/Devices/ZeroDevice.h b/Kernel/Devices/ZeroDevice.h
new file mode 100644
index 0000000000..cfcb00db9c
--- /dev/null
+++ b/Kernel/Devices/ZeroDevice.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "CharacterDevice.h"
+
+class ZeroDevice final : public CharacterDevice {
+ AK_MAKE_ETERNAL
+public:
+ ZeroDevice();
+ virtual ~ZeroDevice() override;
+
+private:
+ // ^CharacterDevice
+ virtual ssize_t read(Process&, byte*, ssize_t) override;
+ virtual ssize_t write(Process&, const byte*, ssize_t) override;
+ virtual bool can_read(Process&) const override;
+ virtual bool can_write(Process&) const override { return true; }
+ virtual const char* class_name() const override { return "ZeroDevice"; }
+};
+