diff --git a/Kernel/Devices/FloppyDiskDevice.cpp b/Kernel/Devices/FloppyDiskDevice.cpp new file mode 100644 index 0000000000..a895359950 --- /dev/null +++ b/Kernel/Devices/FloppyDiskDevice.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include + +// Uncomment me for a LOT of output +//#define FLOPPY_DEBUG + +// THESE ARE OFFSETS! +#define FLOPPY_STATUS_A 0x00 // ro +#define FLOPPY_STATUS_B 0x01 // ro +#define FLOPPY_DOR 0x02 // rw +#define FLOPPY_TDR 0x03 // rw +#define FLOPPY_MSR 0x04 // ro +#define FLOPPY_DSR 0x04 // wo +#define FLOPPY_FIFO 0x05 +#define FLOPPY_RSVD 0x06 +#define FLOPPY_DIR 0x07 // ro +#define FLOPPY_CCR 0x07 // wo + +#define FLOPPY_STATUS_DIR 0x01 +#define FLOPPY_STATUS_WP 0x02 +#define FLOPPY_STATUS_INDX 0x04 +#define FLOPPY_STATUS_HDSEL 0x08 +#define FLOPPY_STATUS_TRK0 0x10 +#define FLOPPY_STATUS_STEP 0x20 +#define FLOPPY_STATUS_DRV2 0x40 +#define FLOPPY_STATUS_INTW 0x80 // A.K.A INT_PENDING + +#define FLOPPY_DOR_DRVSEL0 0x01 +#define FLOPPY_DOR_DRVSEL1 0x02 +#define FLOPPY_DOR_RESET 0x04 +#define FLOPPY_DOR_DMAGATE 0x08 +#define FLOPPY_DOR_MOTEN0 0x10 +#define FLOPPY_DOR_MOTEN1 0x20 +#define FLOPPY_DOR_MOTEN2 0x40 +#define FLOPPY_DOR_MOTEN3 0x80 +// Preset values to activate drive select and motor enable for each drive +#define FLOPPY_DOR_DRV0 0x1C +#define FLOPPY_DOR_DRV1 0x2D +#define FLOPPY_DOR_DRV2 0x4E +#define FLOPPY_DOR_DRV3 0x8F + +#define FLOPPY_MSR_FDD0BSY 0x01 +#define FLOPPY_MSR_FDD1BSY 0x02 +#define FLOPPY_MSR_FDD2BSY 0x04 +#define FLOPPY_MSR_FDD3BSY 0x08 +#define FLOPPY_MSR_FDCBSY 0x10 +#define FLOPPY_MSR_MODE 0x20 // 0 in DMA mode, 1 in PIO mode +#define FLOPPY_MSR_DIO 0x40 // 0 FDC is expecting data from the CPU, 1 if FDC has data for CPU +#define FLOPPY_MSR_RQM 0x80 // 0 Data register not ready, 1 data register ready + +#define FLOPPY_CCR_DRTESEL0 0x01 +#define FLOPPY_CCR_DRTESEL1 0x02 + +#define FLOPPY_MT 0x80 // Multi-track selector. The controller treats 2 tracks (on side 0 and side 1) as a single track instead +#define FLOPPY_MFM 0x40 // 1 Means this disk is double density (double sided??) +#define FLOPPY_SK 0x20 // Skip flag. Skips sectors containing deleted data automatically for us :) + +#define SR0_OKAY (0x00) << 6 +#define SR0_ABORMAL_TERMINATION (0x01) << 6 +#define SR0_INVALID_CMD (0x02) << 6 +#define SR0_ABNORMAL_TERM_POLL (0x03) << 6 + +#define FLOPPY_DMA_CHANNEL 2 // All FDCs are DMA channel 2 +#define IRQ_FLOPPY_DRIVE 6 + +NonnullRefPtr FloppyDiskDevice::create(DriveType type) +{ + return adopt(*new FloppyDiskDevice(type)); +} + +const char* FloppyDiskDevice::class_name() const +{ + if (m_controller_version == 0x90) + return "Intel 82078 Floppy Disk Controller"; + else if (m_controller_version == 0x80) + return "NEC uPD765"; + + return "Generic Floppy Disk Controller"; +} + +FloppyDiskDevice::FloppyDiskDevice(FloppyDiskDevice::DriveType type) + : IRQHandler(IRQ_FLOPPY_DRIVE) + , m_io_base_addr((type == FloppyDiskDevice::DriveType::Master) ? 0x3F0 : 0x370) +{ + initialize(); +} + +FloppyDiskDevice::~FloppyDiskDevice() +{ +} + +unsigned FloppyDiskDevice::block_size() const +{ + return BYTES_PER_SECTOR; +} + +bool FloppyDiskDevice::read_block(unsigned index, u8* data) const +{ + return const_cast(this)->read_blocks(index, 1, data); +} + +bool FloppyDiskDevice::write_block(unsigned index, const u8* data) +{ + return write_sectors_with_dma(index, 1, data); +} + +bool FloppyDiskDevice::read_blocks(unsigned index, u16 count, u8* data) +{ + return read_sectors_with_dma(index, count, data); +} + +bool FloppyDiskDevice::write_blocks(unsigned index, u16 count, const u8* data) +{ + return write_sectors_with_dma(index, count, data); + ; +} + +bool FloppyDiskDevice::read_sectors_with_dma(u16 lba, u16 count, u8* outbuf) +{ + LOCKER(m_lock); // Acquire lock +#ifdef FLOPPY_DEBUG + kprintf("fdc: read_sectors_with_dma lba = %d count = %d\n", lba, count); +#endif + + motor_enable(is_slave() ? 1 : 0); // Should I bother casting this?! + write_ccr(0); + recalibrate(); // Recalibrate the drive + + // We have to wait for about 300ms for the drive to spin up, because of + // the inertia of the motor and diskette. This is only + // important on real hardware + // TODO: Fix this if you want to get it running on real hardware. This code doesn't allow + // time for the disk to spin up. + + //u32 start = PIT::seconds_since_boot(); + //while(start < PIT::seconds_since_boot() + 1) + // ; + + disable_irq(); + + IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1 + IO::out8(0x0B, 0x56); // Begin DMA, Single Transfer, Increment, Auto, FDC -> RAM, Channel 2 + IO::out8(0xA, 0x2); // Unmask channel 2. The transfer will now begin + + // Translate the LBA address into something the FDC understands. + u16 cylinder = lba2cylinder(lba); + u16 head = lba2head(lba); + u16 sector = lba2sector(lba); + +#ifdef FLOPPY_DEBUG + kprintf("fdc: addr = 0x%x c = %d h = %d s = %d\n", lba * BYTES_PER_SECTOR, cylinder, head, sector); +#endif + + // Intel recommends 3 attempts for a read/write + for (int i = 0; i < 3; i++) { + // Now actually send the command to the drive. This is a big one! + send_byte(FLOPPY_MFM | FLOPPY_MT | FLOPPY_SK | static_cast(FloppyCommand::ReadData)); + send_byte(head << 2 | is_slave() ? 1 : 0); + send_byte(cylinder); + send_byte(head); + send_byte(sector); + send_byte(SECTORS_PER_CYLINDER >> 8); // Yikes! + send_byte((sector + 1) >= SECTORS_PER_CYLINDER ? SECTORS_PER_CYLINDER : sector + 1); + send_byte(0x27); // GPL3 value. The Datasheet doesn't really specify the values for this properly... + send_byte(0xff); + + enable_irq(); + + wait_for_irq(); // TODO: See if there was a lockup here via some "timeout counter" + m_interrupted = false; + + // Flush FIFO + read_byte(); + read_byte(); + read_byte(); + u8 cyl = read_byte(); + read_byte(); + read_byte(); + read_byte(); + + if (cyl != cylinder) { +#ifdef FLOPPY_DEBUG + kprintf("fdc: cyl != cylinder (cyl = %d cylinder = %d)! Retrying...\n", cyl, cylinder); +#endif + continue; + } + + // Let the controller know we handled the interrupt + send_byte(FloppyCommand::SenseInterrupt); + u8 st0 = read_byte(); + u8 pcn = read_byte(); + static_cast(st0); + static_cast(pcn); + + memcpy(outbuf, m_dma_buffer_page->paddr().as_ptr(), 512 * count); + //kprintf("fdc: 0x%x\n", *outbuf); + + return true; + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: out of read attempts (check your hardware maybe!?)\n"); +#endif + return false; +} + +bool FloppyDiskDevice::write_sectors_with_dma(u16 lba, u16 count, const u8* inbuf) +{ + LOCKER(m_lock); // Acquire lock +#ifdef FLOPPY_DEBUG + kprintf("fdc: write_sectors_with_dma lba = %d count = %d\n", lba, count); +#endif + + motor_enable(is_slave() ? 1 : 0); // Should I bother casting this?! + write_ccr(0); + recalibrate(); // Recalibrate the drive + + // We have to wait for about 300ms for the drive to spin up, because of + // the inertia of the motor and diskette. + // TODO: Fix this abomination please! + //u32 start = PIT::seconds_since_boot(); + //while(start < PIT::seconds_since_boot() + 1) + // ; + + disable_irq(); + + IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1 + IO::out8(0x0B, 0x5A); // Begin DMA, Single Transfer, Increment, Auto, RAM -> FDC, Channel 2 + IO::out8(0xA, 0x2); // Unmask channel 2. The transfer will now begin + + u16 cylinder = lba2cylinder(lba); + u16 head = lba2head(lba); + u16 sector = lba2sector(lba); + +#ifdef FLOPPY_DEBUG + kprintf("fdc: addr = 0x%x c = %d h = %d s = %d\n", lba * BYTES_PER_SECTOR, cylinder, head, sector); +#endif + + for (int i = 0; i < 3; i++) { + // Now actually send the command to the drive. This is a big one! + send_byte(FLOPPY_MFM | FLOPPY_MT | static_cast(FloppyCommand::WriteData)); + send_byte(head << 2 | is_slave() ? 1 : 0); + send_byte(cylinder); + send_byte(head); + send_byte(sector); + send_byte(SECTORS_PER_CYLINDER >> 8); // Yikes! + send_byte((sector + 1) >= SECTORS_PER_CYLINDER ? SECTORS_PER_CYLINDER : sector + 1); + send_byte(0x27); // GPL3 value. The Datasheet doesn't really specify the values for this properly... + send_byte(0xff); + + enable_irq(); + + wait_for_irq(); // TODO: See if there was a lockup here via some "timeout counter" + m_interrupted = false; + + // Flush FIFO + read_byte(); + read_byte(); + read_byte(); + u8 cyl = read_byte(); + read_byte(); + read_byte(); + read_byte(); + + if (cyl != cylinder) { +#ifdef FLOPPY_DEBUG + kprintf("fdc: cyl != cylinder (cyl = %d cylinder = %d)! Retrying...\n", cyl, cylinder); +#endif + continue; + } + + // Let the controller know we handled the interrupt + send_byte(FloppyCommand::SenseInterrupt); + u8 st0 = read_byte(); + u8 pcn = read_byte(); + static_cast(st0); + static_cast(pcn); + + memcpy(m_dma_buffer_page->paddr().as_ptr(), inbuf, 512 * count); + + return true; + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: out of read attempts (check your hardware maybe!?)\n"); +#endif + return false; +} + +bool FloppyDiskDevice::wait_for_irq() +{ +#ifdef FLOPPY_DEBUG + kprintf("fdc: Waiting for interrupt...\n"); +#endif + + while (!m_interrupted) { + Scheduler::yield(); + } + + memory_barrier(); + return true; +} + +void FloppyDiskDevice::handle_irq() +{ + // The only thing we need to do is acknowledge the IRQ happened + m_interrupted = true; + +#ifdef FLOPPY_DEBUG + kprintf("fdc: Received IRQ!\n"); +#endif +} + +void FloppyDiskDevice::send_byte(u8 value) const +{ + for (int i = 0; i < 1024; i++) { + if (read_msr() & FLOPPY_MSR_RQM) { + IO::out8(m_io_base_addr + FLOPPY_FIFO, value); + return; + } + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: FIFO write timed out!\n"); +#endif +} + +void FloppyDiskDevice::send_byte(FloppyCommand value) const +{ + for (int i = 0; i < 1024; i++) { + if (read_msr() & FLOPPY_MSR_RQM) { + IO::out8(m_io_base_addr + FLOPPY_FIFO, static_cast(value)); + return; + } + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: FIFO write timed out!\n"); +#endif +} + +u8 FloppyDiskDevice::read_byte() const +{ + for (int i = 0; i < 1024; i++) { + if (read_msr() & (FLOPPY_MSR_RQM | FLOPPY_MSR_DIO)) { + return IO::in8(m_io_base_addr + FLOPPY_FIFO); + } + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: FIFO read timed out!\n"); +#endif + + return 0xff; +} + +void FloppyDiskDevice::write_dor(u8 value) const +{ + IO::out8(m_io_base_addr + FLOPPY_DOR, value); +} + +void FloppyDiskDevice::write_ccr(u8 value) const +{ + IO::out8(m_io_base_addr + FLOPPY_CCR, value); +} + +u8 FloppyDiskDevice::read_msr() const +{ + return IO::in8(m_io_base_addr + FLOPPY_MSR); +} + +void FloppyDiskDevice::motor_enable(bool slave) const +{ + u8 val = slave ? 0x1C : 0x2D; + write_dor(val); +} + +bool FloppyDiskDevice::is_busy() const +{ + return read_msr() & FLOPPY_MSR; +} + +bool FloppyDiskDevice::recalibrate() +{ +#ifdef FLOPPY_DEBUG + kprintf("fdc: recalibrating drive...\n"); +#endif + + u8 slave = is_slave() ? 1 : 0; + motor_enable(slave); + + for (int i = 0; i < 16; i++) { + send_byte(FloppyCommand::Recalibrate); + send_byte(slave); + wait_for_irq(); + m_interrupted = false; + + send_byte(FloppyCommand::Recalibrate); + u8 st0 = read_byte(); + u8 pcn = read_byte(); + static_cast(st0); + + if (pcn == 0) + return true; + } + +#ifdef FLOPPY_DEBUG + kprintf("fdc: failed to calibrate drive (check your hardware!)\n"); +#endif + return false; +} + +bool FloppyDiskDevice::seek(u16 lba) +{ + u8 head = lba2head(lba) & 0x01; + u8 cylinder = lba2cylinder(lba) & 0xff; + u8 slave = is_slave() ? 1 : 0; + + // First, we need to enable the correct drive motor + motor_enable(slave); +#ifdef FLOPPY_DEBUG + kprintf("fdc: seeking to cylinder %d on side %d on drive %d\n", cylinder, head, slave); +#endif + + // Try at most 5 times to seek to the desired cylinder + for (int attempt = 0; attempt < 5; attempt++) { + send_byte(FloppyCommand::Seek); + send_byte((head << 2) | slave); + send_byte(cylinder); + wait_for_irq(); + m_interrupted = false; + + send_byte(FloppyCommand::SenseInterrupt); + u8 st0 = read_byte(); + u8 pcn = read_byte(); + + if ((st0 >> 5) != 1 || pcn != cylinder || (st0 & 0x01)) { +#ifdef FLOPPY_DEBUG + kprintf("fdc: failed to seek to cylinder %d on attempt %d!\n", cylinder, attempt); +#endif + continue; + } + + return true; + } + + kprintf("fdc: failed to seek after 3 attempts! Aborting...\n"); + return false; +} + +// This is following Intel's datasheet for the 82077, page 41 +void FloppyDiskDevice::initialize() +{ +#ifdef FLOPPY_DEBUG + kprintf("fdc: m_io_base = 0x%x IRQn = %d\n", m_io_base_addr, IRQ_FLOPPY_DRIVE); +#endif + + enable_irq(); + + // Get the version of the Floppy Disk Controller + send_byte(FloppyCommand::Version); + m_controller_version = read_byte(); + kprintf("fdc: Version = 0x%x\n", m_controller_version); + + // Reset + write_dor(0); + write_dor(FLOPPY_DOR_RESET | FLOPPY_DOR_DMAGATE); + + write_ccr(0); + wait_for_irq(); + m_interrupted = false; + + // "If (and only if) drive polling mode is turned on, send 4 Sense Interrupt commands (required). " + // Sorry OSDev, but the Intel Manual states otherwise. This ALWAYS needs to be performed. + for (int i = 0; i < 4; i++) { + send_byte(FloppyCommand::SenseInterrupt); + u8 sr0 = read_byte(); + u8 trk = read_byte(); + + kprintf("sr0 = 0x%x, cyl = 0x%x\n", sr0, trk); + } + + // This is hardcoded for a 3.5" floppy disk drive + send_byte(FloppyCommand::Specify); + send_byte(0x08); // (SRT << 4) | HUT + send_byte(0x0A); // (HLT << 1) | NDMA + + // Allocate a buffer page for us to read into. This only needs to be one sector in size. + m_dma_buffer_page = MM.allocate_supervisor_physical_page(); +#ifdef FLOPPY_DEBUG + kprintf("fdc: allocated supervisor page at paddr 0x%x\n", m_dma_buffer_page->paddr()); +#endif + + // Now, let's initialise channel 2 of the DMA controller! + // This only needs to be done here, then we can just change the direction of + // the transfer + IO::out8(0xA, FLOPPY_DMA_CHANNEL | 0x4); // Channel 2 SEL, MASK_ON = 1 + + IO::out8(0xC, 0xFF); // Reset Master Flip Flop + + // Set the buffer page address (the lower 16-bits) + IO::out8(0x4, m_dma_buffer_page->paddr().get() & 0xff); + IO::out8(0x4, (m_dma_buffer_page->paddr().get() >> 8) & 0xff); + + IO::out8(0xC, 0xFF); // Reset Master Flip Flop again + + IO::out8(0x05, (SECTORS_PER_CYLINDER * BYTES_PER_SECTOR) & 0xff); + IO::out8(0x05, (SECTORS_PER_CYLINDER * BYTES_PER_SECTOR) >> 8); + IO::out8(0x81, (m_dma_buffer_page->paddr().get() >> 16) & 0xff); // Supervisor page could be a 24-bit address, so set the External Page R/W register + + IO::out8(0xA, 0x2); // Unmask Channel 2 + +#ifdef FLOPPY_DEBUG + kprintf("fdc: fd%d initialised succesfully!\n", is_slave() ? 1 : 0); +#endif +} diff --git a/Kernel/Devices/FloppyDiskDevice.h b/Kernel/Devices/FloppyDiskDevice.h new file mode 100644 index 0000000000..8120415d68 --- /dev/null +++ b/Kernel/Devices/FloppyDiskDevice.h @@ -0,0 +1,190 @@ +// +// Intel 82078 Floppy Disk controller driver +// Author: Jesse Buhagiar [quaker762] +// Datasheet: https://wiki.qemu.org/images/f/f0/29047403.pdf +// Email me at jooster669@gmail.com if you have any questions/suggestions :) +// +// The Intel 82078 is a 44-pin package, CHMOS Single Chip Floppy Disk Controller found commonly +// on later PCs in the mid to late 90s. It supports a multitude of floppy drives found in computers +// at the time, up to and including 2.8MB ED Floppy Disks and is software compatible with previous FDCs. +// Drive in this case refers to the actual drive where the media is inserted and a disk is the actual +// magnetic floppy disk media. This controller is emulated by QEMU. +// +// Certain terminology exists in the code of this driver that may be confusing, being that there +// is a lot of code and documentation online that is seemingly conflicting. I've used terms found +// directly in the datasheet however for the sake of completeness I'll explain them here: +// +// - Cylinder: One full circular 'slice' of the floppy disk. It contains 18 sectors +// on a 3.5" floppy disk. It is also known as a 'track'. There are +// 80 tracks on a single side of a floppy disk. +// - Sector: One 512 byte chunk of a track. +// - Head: The read write arm found inside the drive itself. On a double sided +// floppy disk drive, there are two, one for the top tracks of the disk +// and the other for the bottom tracks. +// - CHS: Cylinder, Head, Sector. The addressing type this floppy controller +// uses to address the disk geometry. +// +// A normal PC System usually contains one or two floppy drives. This controller contains the +// ability to control up to four drives with the one controller, however it is very rare for +// most systems to contain this amount of drives. +// +// The basic operation of the drive involves reseting the drive in hardware, then sending command +// bytes to the FIFO, allowing the command to execute, then flushing the FIFO by reading `n` bytes +// from it. Most commands are multi-parameter and multi-result, so it's best to consult the datasheet +// from page 23. It is recommended that a SENSE command is performed to retrieve valubable interrupt +// information about the performed action. +// +// Reseting the controller involves the following: +// - Acquire the version ID of the controller. +// - Reset the DOR register +// - Deassert software reset bit in the DOR register and assert the DMAGATE pin to initialize DMA mode +// - Program the Configuration Control Register (CCR) for 3.5" 1.44MB diskettes +// - Send a SPECIFY command to specify more drive information. Refer to the datasheet +// +// The drive (being mapped to the controller) will then be in a state that will accept the correct media. +// The DMA controller is also set up here, which is on channel 2. This only needs to be done once, the +// read and write commands can toggle the appropriate bits themselves to allow a specific transfer direction. +// +// Recalibrating the drive refers to the act of resetting the head of the drive back to track/cylinder 0. It +// is essentially the same as a seek, however returning the drive to a known position. For the sake of brevity, +// only the recalibrate sequence will be described. +// +// - Enable the drive and it's motor (all drive motors are manually enabled by us!). +// - Issue a recalibrate or a seek command +// - Wait for interrupt +// - Issue a SENSE command, letting the drive know we handled the interrupt +// - Flush the FIFO and check the cylinder value to ensure we are at the correct spot. +// +// Once this has been completed, the drive will either be at the desired position or back at cylinder 0. +// +// To perform a READ or a WRITE of the diskette inserted, the following actions must be taken: +// +// -The drive and it's motor must be enabled +// -The data rate must be set via CCR +// -The drive must be then recalibrated to ensure the head has not drifted. +// -A wait of 500ms or greater must occur to allow the drive to spin up from inertia. +// -The DMA direction of the transfer is then configured. +// -The READ or WRITE command is issued to the controller. +// -A timeout counter is started. This is only for real hardware and is currently not implemented. +// -Read the result bytes. +// -Attempt to READ or WRITE to the disk. Intel recommends doing this a max of 3 times before failing. +// +// +// +#pragma once + +#include +#include +#include +#include +#include +#include + +struct FloppyControllerCommand { + u8 cmd; // Command to send to the controller + u8 numParams; // Number of parameters to send to the drive + u8 numReturned; // Number of values we expect to be returned by the command + u8* params; + u8* result; +}; + +// +// NOTE: This class only supports 3.5" 1.44MB floppy disks! +// Any other type of drive will be ignored +// +// Also not that the floppy disk controller is set up to be in PS/2 mode, which +// uses the Intel 82077A controller. More about this controller can +// be found here: http://www.buchty.net/casio/files/82077.pdf +// +class FloppyDiskDevice final : public IRQHandler + , public DiskDevice { + AK_MAKE_ETERNAL + + static constexpr u8 SECTORS_PER_CYLINDER = 18; + static constexpr u8 CCYLINDERS_PER_HEAD = 80; + static constexpr u16 BYTES_PER_SECTOR = 512; + +public: + // + // Is this floppy drive the master or the slave on the controller?? + // + enum class DriveType : u8 { + Master, + Slave + }; + +private: + // Floppy commands + enum class FloppyCommand : u8 { + ReadTrack = 0x02, + Specify = 0x03, + CheckStatus = 0x04, + WriteData = 0x05, + ReadData = 0x06, + Recalibrate = 0x07, + SenseInterrupt = 0x08, + WriteDeletedData = 0x09, + ReadDeletedData = 0x0C, + FormatTrack = 0x0D, + Seek = 0x0F, + Version = 0x10, + Verify = 0x16, + }; + +public: + static NonnullRefPtr create(DriveType type); + virtual ~FloppyDiskDevice() override; + + // ^DiskDevice + virtual unsigned block_size() const override; + virtual bool read_block(unsigned index, u8*) const override; + virtual bool write_block(unsigned index, const u8*) override; + virtual bool read_blocks(unsigned index, u16 count, u8*) override; + virtual bool write_blocks(unsigned index, u16 count, const u8*) override; + +protected: + explicit FloppyDiskDevice(DriveType); + +private: + // ^IRQHandler + void handle_irq(); + + // ^DiskDevice + virtual const char* class_name() const override; + + // Helper functions + inline u16 lba2cylinder(u16 lba) const { return lba / (2 * SECTORS_PER_CYLINDER); } // Convert an LBA into a cylinder value + inline u16 lba2head(u16 lba) const { return ((lba / SECTORS_PER_CYLINDER) % 2); } // Convert an LBA into a head value + inline u16 lba2sector(u16 lba) const { return ((lba % SECTORS_PER_CYLINDER) + 1); } // Convert an LBA into a sector value + + void initialize(); + bool read_sectors_with_dma(u16, u16, u8*); + bool write_sectors_with_dma(u16, u16, const u8*); + bool wait_for_irq(); + + bool is_busy() const; + bool seek(u16); + bool recalibrate(); + + void send_byte(u8) const; + void send_byte(FloppyCommand) const; + + void write_dor(u8) const; + void write_ccr(u8) const; + void motor_enable(bool) const; + void configure_drive(u8, u8, u8) const; + + u8 read_byte() const; + u8 read_msr() const; + + bool is_slave() const { return m_drive_type == DriveType::Slave; } + + Lock m_lock { "FloppyDiskDevice" }; + u16 m_io_base_addr { 0 }; + volatile bool m_interrupted { false }; + + DriveType m_drive_type { DriveType::Master }; + RefPtr m_dma_buffer_page; + + u8 m_controller_version { 0 }; +}; diff --git a/Kernel/Makefile b/Kernel/Makefile index 272334ba08..a508a8cc24 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -14,6 +14,7 @@ KERNEL_OBJS = \ Arch/i386/PIC.o \ Syscall.o \ Devices/IDEDiskDevice.o \ + Devices/FloppyDiskDevice.o \ VM/MemoryManager.o \ VM/Region.o \ VM/VMObject.o \ diff --git a/Kernel/init.cpp b/Kernel/init.cpp index b580fcfac3..c2bba580c4 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -136,6 +138,24 @@ VFS* vfs; vfs->mount(ProcFS::the(), "/proc"); vfs->mount(DevPtsFS::the(), "/dev/pts"); + // Now, detect whether or not there are actually any floppy disks attached to the system + u8 detect = CMOS::read(0x10); + RefPtr fd0; + RefPtr fd1; + if ((detect >> 4) & 0x4) { + fd0 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Master); + kprintf("fd0 is 1.44MB floppy drive\n"); + } else { + kprintf("fd0 type unsupported! Type == 0x%x\n", detect >> 4); + } + + if (detect & 0x0f) { + fd0 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Slave); + kprintf("fd1 is 1.44MB floppy drive"); + } else { + kprintf("fd1 type unsupported! Type == 0x%x\n", detect & 0x0f); + } + int error; auto* system_server_process = Process::create_user_process("/bin/SystemServer", (uid_t)100, (gid_t)100, (pid_t)0, error, {}, {}, tty0);