diff options
author | Jesse <jesse.buhagiar@student.rmit.edu.au> | 2019-07-17 23:51:51 +1000 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-07-17 15:51:51 +0200 |
commit | 10ffaf019f7437f86dca0bbcc5e983c4bbabc824 (patch) | |
tree | d73ed21614a8f93f596e02308b5cd5c791d1890a /Kernel/Devices/FloppyDiskDevice.h | |
parent | 4f94fbc9e1dc9563a03e3464ee3b03ac5d835d92 (diff) | |
download | serenity-10ffaf019f7437f86dca0bbcc5e983c4bbabc824.zip |
Kernel: Initial FDC Device Driver (#315)
A basic Floppy Disk Controller device driver for any system later than (and including) the IBM AT. The driver is based on the documentation supplied by QEMU, which is the datasheet for the Intel 82078 Floppy Disk controller (found here: https://wiki.qemu.org/images/f/f0/29047403.pdf)
Naturally, floppy disks are a _very_ outdated storage medium, however, as Serenity is a throwback to aesthetic 90s computing, it's a definite must have. Not to mention that there are still a lot of floppy disks around, with countless petabytes of software on them, so it would be nice if people could create images of said disks with serenity.
The code for this is mostly clean. however there are a LOT of values specified in the datasheet, so some of them might be wrong, not to mention that the actual specification itself is rather dirt and seemingly hacked together.
I'm also only supporting 3.5" floppy disks, without PIO polling (DMA only), so if you want anything more/less than 1.44MB HD Floppys, you'll have to do it yourself.
Diffstat (limited to 'Kernel/Devices/FloppyDiskDevice.h')
-rw-r--r-- | Kernel/Devices/FloppyDiskDevice.h | 190 |
1 files changed, 190 insertions, 0 deletions
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 <AK/RefPtr.h> +#include <Kernel/Devices/DiskDevice.h> +#include <Kernel/IRQHandler.h> +#include <Kernel/Lock.h> +#include <Kernel/VM/PhysicalAddress.h> +#include <Kernel/VM/PhysicalPage.h> + +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<FloppyDiskDevice> 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<PhysicalPage> m_dma_buffer_page; + + u8 m_controller_version { 0 }; +}; |