diff options
-rw-r--r-- | Kernel/Devices/FloppyDiskDevice.cpp | 565 | ||||
-rw-r--r-- | Kernel/Devices/FloppyDiskDevice.h | 224 | ||||
-rw-r--r-- | Kernel/Makefile | 1 | ||||
-rw-r--r-- | Kernel/init.cpp | 19 |
4 files changed, 0 insertions, 809 deletions
diff --git a/Kernel/Devices/FloppyDiskDevice.cpp b/Kernel/Devices/FloppyDiskDevice.cpp deleted file mode 100644 index 6e519d37b3..0000000000 --- a/Kernel/Devices/FloppyDiskDevice.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <AK/Memory.h> -#include <Kernel/Devices/FloppyDiskDevice.h> -#include <Kernel/VM/MemoryManager.h> -#include <LibBareMetal/IO.h> - -namespace Kernel { - -// 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> 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) - , BlockDevice(89, (type == FloppyDiskDevice::DriveType::Master) ? 0 : 1, BYTES_PER_SECTOR) - , m_io_base_addr((type == FloppyDiskDevice::DriveType::Master) ? 0x3F0 : 0x370) -{ - initialize(); -} - -FloppyDiskDevice::~FloppyDiskDevice() -{ -} - -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 - klog() << "fdc: read_sectors_with_dma lba = " << lba << " count = " << count; -#endif - - motor_enable(is_slave()); // Should I bother casting this?! - write_ccr(0); - recalibrate(); - - if (!seek(lba)) { - klog() << "fdc: failed to seek to lba = " << lba << "!"; - return false; - } - - // 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 - klog() << "fdc: addr = 0x" << String::format("%x", lba * BYTES_PER_SECTOR) << " c = " << cylinder << " h = " << head << " s = " << 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<u8>(FloppyCommand::ReadData)); - send_byte((head << 2) | is_slave()); - 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(0x1b); // 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 - // Let's check the value of Status Register 1 to ensure that - // the command executed correctly - u8 cmd_st0 = read_byte(); - if ((cmd_st0 & 0xc0) != 0) { - klog() << "fdc: read failed with error code (st0) 0x" << String::format("%x", cmd_st0 >> 6); - return false; - } - - u8 cmd_st1 = read_byte(); - if (cmd_st1 != 0) { - klog() << "fdc: read failed with error code (st1) 0x" << String::format("%x", cmd_st1); - return false; - } - - read_byte(); - u8 cyl = read_byte(); - read_byte(); - read_byte(); - read_byte(); - - if (cyl != cylinder) { -#ifdef FLOPPY_DEBUG - klog() << "fdc: cyl != cylinder (cyl = " << cyl << " cylinder = " << cylinder << ")! Retrying..."; -#endif - continue; - } - - // Let the controller know we handled the interrupt - send_byte(FloppyCommand::SenseInterrupt); - u8 st0 = read_byte(); - u8 pcn = read_byte(); - static_cast<void>(st0); - static_cast<void>(pcn); - - memcpy(outbuf, m_dma_buffer_page->paddr().as_ptr(), 512 * count); - - return true; - } - -#ifdef FLOPPY_DEBUG - klog() << "fdc: out of read attempts (check your hardware maybe!?)"; -#endif - return false; -} - -bool FloppyDiskDevice::write_sectors_with_dma(u16 lba, u16 count, const u8* inbuf) -{ - LOCKER(m_lock); // Acquire lock -#ifdef FLOPPY_DEBUG - klog() << "fdc: write_sectors_with_dma lba = " << lba << " count = " << count; -#endif - - motor_enable(is_slave() ? 1 : 0); // Should I bother casting this?! - write_ccr(0); - recalibrate(); // Recalibrate the drive - - if (!seek(lba)) { - klog() << "fdc: failed to seek to lba = " << lba << "!"; - return false; - } - - // 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 - klog() << "fdc: addr = 0x" << String::format("%x", lba * BYTES_PER_SECTOR) << " c = " << cylinder << " h = " << head << " s = " << 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<u8>(FloppyCommand::WriteData)); - send_byte(head << 2 | is_slave()); - 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(0x1b); // 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 - u8 cmd_st0 = read_byte(); - if ((cmd_st0 & 0xc0) != 0) { - klog() << "fdc: write failed! Error code 0x" << String::format("%x", cmd_st0 >> 6); - return false; - } - - u8 cmd_st1 = read_byte(); - if (cmd_st1 != 0) { - klog() << "fdc: write failed with error code (st1) 0x" << String::format("%x", cmd_st1); - return false; - } - - read_byte(); - u8 cyl = read_byte(); - read_byte(); - read_byte(); - read_byte(); - - if (cyl != cylinder) { -#ifdef FLOPPY_DEBUG - klog() << "fdc: cyl != cylinder (cyl = " << cyl << " cylinder = " << cylinder << ")! Retrying..."; -#endif - continue; - } - - // Let the controller know we handled the interrupt - send_byte(FloppyCommand::SenseInterrupt); - u8 st0 = read_byte(); - u8 pcn = read_byte(); - static_cast<void>(st0); - static_cast<void>(pcn); - - memcpy(m_dma_buffer_page->paddr().as_ptr(), inbuf, 512 * count); - - return true; - } - -#ifdef FLOPPY_DEBUG - klog() << "fdc: out of read attempts (check your hardware maybe!?)"; -#endif - return false; -} - -bool FloppyDiskDevice::wait_for_irq() -{ -#ifdef FLOPPY_DEBUG - klog() << "fdc: Waiting for interrupt..."; -#endif - - while (!m_interrupted) { - Scheduler::yield(); - } - - memory_barrier(); - return true; -} - -void FloppyDiskDevice::handle_irq(const RegisterState&) -{ - // The only thing we need to do is acknowledge the IRQ happened - m_interrupted = true; - -#ifdef FLOPPY_DEBUG - klog() << "fdc: Received IRQ!"; -#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 - klog() << "fdc: FIFO write timed out!"; -#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<u8>(value)); - return; - } - } - -#ifdef FLOPPY_DEBUG - klog() << "fdc: FIFO write timed out!"; -#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 - klog() << "fdc: FIFO read timed out!"; -#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 ? 0x2D : 0x1C; - write_dor(val); -} - -bool FloppyDiskDevice::is_busy() const -{ - return read_msr() & FLOPPY_MSR; -} - -bool FloppyDiskDevice::recalibrate() -{ -#ifdef FLOPPY_DEBUG - klog() << "fdc: recalibrating drive..."; -#endif - - u8 slave = is_slave(); - 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::SenseInterrupt); - u8 st0 = read_byte(); - u8 pcn = read_byte(); - static_cast<void>(st0); - - if (pcn == 0) - return true; - } - -#ifdef FLOPPY_DEBUG - klog() << "fdc: failed to calibrate drive (check your hardware!)"; -#endif - return false; -} - -bool FloppyDiskDevice::seek(u16 lba) -{ - u8 head = lba2head(lba) & 0x01; - u8 cylinder = lba2cylinder(lba) & 0xff; - u8 slave = is_slave(); - - // First, we need to enable the correct drive motor - motor_enable(slave); -#ifdef FLOPPY_DEBUG - klog() << "fdc: seeking to cylinder " << cylinder << " on side " << head << " on drive " << 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 - klog() << "fdc: failed to seek to cylinder " << cylinder << " on attempt " << attempt << "!"; -#endif - continue; - } - - return true; - } - - klog() << "fdc: failed to seek after 3 attempts! Aborting..."; - return false; -} - -// This is following Intel's datasheet for the 82077, page 41 -void FloppyDiskDevice::initialize() -{ -#ifdef FLOPPY_DEBUG - klog() << "fdc: m_io_base = 0x" << String::format("%x", m_io_base_addr) << " IRQn = " << IRQ_FLOPPY_DRIVE; -#endif - - enable_irq(); - - // Get the version of the Floppy Disk Controller - send_byte(FloppyCommand::Version); - m_controller_version = read_byte(); - klog() << "fdc: Version = 0x" << String::format("%x", 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(); - - klog() << "sr0 = 0x" << String::format("%x", sr0) << ", cyl = 0x" << String::format("%x", 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 - klog() << "fdc: allocated supervisor page at paddr 0x", String::format("%x", 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 - klog() << "fdc: fd" << (is_slave() ? 1 : 0) << " initialised succesfully!"; -#endif -} - -} diff --git a/Kernel/Devices/FloppyDiskDevice.h b/Kernel/Devices/FloppyDiskDevice.h deleted file mode 100644 index a7c0dcffff..0000000000 --- a/Kernel/Devices/FloppyDiskDevice.h +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (c) 2019-2020, Jesse Buhagiar <jooster669@gmail.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// -// Intel 82078 Floppy Disk controller driver -// Datasheet: https://wiki.qemu.org/images/f/f0/29047403.pdf -// -// 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 <AK/RefPtr.h> -#include <Kernel/Devices/BlockDevice.h> -#include <Kernel/Interrupts/IRQHandler.h> -#include <Kernel/Lock.h> -#include <Kernel/VM/PhysicalPage.h> -#include <LibBareMetal/Memory/PhysicalAddress.h> - -namespace Kernel { - -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 BlockDevice { - AK_MAKE_ETERNAL - - static constexpr u8 SECTORS_PER_CYLINDER = 18; - static constexpr u8 CYLINDERS_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<FloppyDiskDevice> create(DriveType); - virtual ~FloppyDiskDevice() override; - - // ^DiskDevice - virtual bool read_blocks(unsigned index, u16 count, u8*) override; - virtual bool write_blocks(unsigned index, u16 count, const u8*) override; - - // ^BlockDevice - virtual ssize_t read(FileDescription&, u8*, ssize_t) override { return 0; } - virtual bool can_read(const FileDescription&) const override { return true; } - virtual ssize_t write(FileDescription&, const u8*, ssize_t) override { return 0; } - virtual bool can_write(const FileDescription&) const override { return true; } - - virtual const char* purpose() const override { return "Floppy Disk Controller"; } - -protected: - explicit FloppyDiskDevice(DriveType); - -private: - // ^IRQHandler - virtual void handle_irq(const RegisterState&) override; - - // ^DiskDevice - virtual const char* class_name() const override; - - // Helper functions - inline u16 lba2head(u16 lba) const { return (lba % (SECTORS_PER_CYLINDER * 2)) / SECTORS_PER_CYLINDER; } // Convert an LBA into a head value - inline u16 lba2cylinder(u16 lba) const { return lba / (2 * SECTORS_PER_CYLINDER); } // Convert an LBA into a cylinder 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<PhysicalPage> m_dma_buffer_page; - - u8 m_controller_version { 0 }; -}; - -} diff --git a/Kernel/Makefile b/Kernel/Makefile index 72790695ac..7b97e15ff6 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -37,7 +37,6 @@ OBJS = \ Devices/DebugLogDevice.o \ Devices/Device.o \ Devices/DiskPartition.o \ - Devices/FloppyDiskDevice.o \ Devices/FullDevice.o \ Devices/GPTPartitionTable.o \ Devices/EBRPartitionTable.o \ diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 24208a3d7a..5b9777d3c0 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -40,7 +40,6 @@ #include <Kernel/Devices/DebugLogDevice.h> #include <Kernel/Devices/DiskPartition.h> #include <Kernel/Devices/EBRPartitionTable.h> -#include <Kernel/Devices/FloppyDiskDevice.h> #include <Kernel/Devices/FullDevice.h> #include <Kernel/Devices/GPTPartitionTable.h> #include <Kernel/Devices/KeyboardDevice.h> @@ -310,24 +309,6 @@ void init_stage2() load_ksyms(); dbg() << "Loaded ksyms"; - // Now, detect whether or not there are actually any floppy disks attached to the system - u8 detect = CMOS::read(0x10); - RefPtr<FloppyDiskDevice> fd0; - RefPtr<FloppyDiskDevice> fd1; - if ((detect >> 4) & 0x4) { - fd0 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Master); - klog() << "fd0 is 1.44MB floppy drive"; - } else { - klog() << "fd0 type unsupported! Type == 0x", String::format("%x", detect >> 4); - } - - if (detect & 0x0f) { - fd1 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Slave); - klog() << "fd1 is 1.44MB floppy drive"; - } else { - klog() << "fd1 type unsupported! Type == 0x", String::format("%x", detect & 0x0f); - } - int error; // SystemServer will start WindowServer, which will be doing graphics. |