diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-04-03 12:36:40 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-04-03 12:36:40 +0200 |
commit | ab43658c5537c03d075f8abc57fe90e940023779 (patch) | |
tree | 01bd0f73784dd42372dd8a8f065a3a8c6b3d9858 /Kernel/Devices | |
parent | 072ea7eece8607efec8e2824307b0a6209e4d014 (diff) | |
download | serenity-ab43658c5537c03d075f8abc57fe90e940023779.zip |
Kernel: Move devices into Kernel/Devices/.
Diffstat (limited to 'Kernel/Devices')
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"; } +}; + |