Attempt 2 #2

Open
seeseemelk wants to merge 56 commits from attempt-2 into master
6 changed files with 859 additions and 1 deletions
Showing only changes of commit 31740f7556 - Show all commits

View File

@@ -64,7 +64,7 @@ Once a task is completed, it should be checked off.
- [x] Create a `sysfs` vfs driver. It should allow drivers to expose text/config files to the VFS. Each driver can request a namespace in the sysfs. E.g.: the IDE driver could request `ide`. During this registration, the drive must provide a struct containing function callbacks. The callbacks must contain the function `list`, `read`, and `write`. These are executed when the user lists a directory, reads a file, or writes a file. It is expected that the contents of these files are extremely small and can simply be stored on the stack. It should be very easy for a driver to expose new information.
- [x] Create a FAT32 driver. It should allow reading and writing to and from a block device.
- [x] Create the `mount` app. It should allow on to mount a block device using the fat32 driver. Internally, it should use sysfs (which should be mounted automatically by the kernel to `/sys`) to setup a new mount.
- [ ] Create a floppy driver. Each floppy device should be exposed as `/dev/floppyN`.
- [x] Create a floppy driver. Each floppy device should be exposed as `/dev/floppyN`.
- [ ] Add support for character device to the devicefs subsystem.
- [ ] Create an app called `diskpart`. This app can be used to modify the MBR partitions on a block device.
- [ ] Create an app called `mkfs.fat32`. This app can be used to format a block into a FAT32 filesystem.

43
build.log Normal file
View File

@@ -0,0 +1,43 @@
[ 3%] Building user-mode applications
Building app: cat
Built: /workspaces/claude-os/build/apps_bin/cat (310 bytes)
Building app: env-test
/usr/bin/ld: warning: /workspaces/claude-os/build/apps_bin/env-test.elf has a LOAD segment with RWX permissions
Built: /workspaces/claude-os/build/apps_bin/env-test (389 bytes)
Building app: fork-test
Built: /workspaces/claude-os/build/apps_bin/fork-test (132 bytes)
Building app: hello-world
Built: /workspaces/claude-os/build/apps_bin/hello-world (49 bytes)
Building app: ls
Built: /workspaces/claude-os/build/apps_bin/ls (250 bytes)
Building app: mount
Built: /workspaces/claude-os/build/apps_bin/mount (992 bytes)
Building app: sh
/workspaces/claude-os/apps/sh/sh.c:167:17: warning: unused variable 'type' [-Wunused-variable]
167 | int32_t type = readdir(resolved, 0, name);
| ^~~~
1 warning generated.
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
[ 3%] Built target apps
[ 6%] Built target initrd
[ 9%] Building C object src/CMakeFiles/kernel.dir/floppy.c.o
[ 12%] Linking C executable ../bin/kernel
[ 96%] Built target kernel
[100%] Generating bootable ISO image
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso'
Media current: stdio file, overwriteable
Media status : is blank
Media summary: 0 sessions, 0 data blocks, 0 data, 127g free
Added to ISO image: directory '/'='/tmp/grub.oPDKGd'
xorriso : UPDATE : 581 files added in 1 seconds
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
xorriso : UPDATE : 586 files added in 1 seconds
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
xorriso : UPDATE : Thank you for being patient. Working since 0 seconds.
ISO image produced: 5918 sectors
Written to medium : 5918 sectors at LBA 0
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
[100%] Built target iso

View File

@@ -24,6 +24,7 @@ add_executable(kernel
ide.c
mbr.c
fat32.c
floppy.c
env.c
keyboard.c
interrupts.S

688
src/floppy.c Normal file
View File

@@ -0,0 +1,688 @@
/**
* @file floppy.c
* @brief Floppy disk controller driver implementation.
*
* Drives the Intel 82077AA-compatible floppy disk controller using
* ISA DMA channel 2 for data transfers and IRQ 6 for completion
* notification.
*
* Supports 1.44 MB 3.5" HD floppies. The driver detects drives via
* CMOS register 0x10, initializes the controller, and registers each
* detected drive with the devicefs subsystem as a "floppy" class
* block device (floppy1, floppy2, ...).
*
* LBA addressing is converted to CHS for the floppy controller.
*/
#include "floppy.h"
#include "port_io.h"
#include "pic.h"
#include "devicefs.h"
#include "driver.h"
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/* ================================================================
* Global state
* ================================================================ */
/** Detected floppy drives. */
static floppy_drive_t floppy_drives[FLOPPY_MAX_DRIVES];
/** Volatile flag set by IRQ 6 handler. */
static volatile int floppy_irq_received = 0;
/** DMA bounce buffer — must be in the first 16 MB of physical memory
* and must not cross a 64 KB boundary. We use a static buffer that
* the linker places in the kernel BSS (which lives in low memory). */
static uint8_t dma_buffer[FLOPPY_SECTOR_SIZE] __attribute__((aligned(512)));
/* ================================================================
* Low-level helpers
* ================================================================ */
/**
* Read CMOS register.
*
* @param reg CMOS register address (0x00-0x7F).
* @return Register value.
*/
static uint8_t cmos_read(uint8_t reg) {
outb(0x70, reg);
/* Small delay for CMOS to respond */
inb(0x80);
return inb(0x71);
}
/**
* Wait for IRQ 6 with timeout.
* The floppy controller fires IRQ 6 on command completion.
*
* If interrupts are currently disabled (e.g. during early init before
* the kernel calls STI), we temporarily enable them so IRQ 6 can
* actually fire, then restore the previous interrupt state.
*
* @return 0 on success (IRQ received), -1 on timeout.
*/
static int fdc_wait_irq(void) {
/* Check whether interrupts are currently enabled (IF flag) */
uint32_t eflags;
asm volatile("pushfl; popl %0" : "=r"(eflags));
int need_sti = !(eflags & 0x200);
if (need_sti) {
asm volatile("sti");
}
int timeout = 5000000;
while (!floppy_irq_received && --timeout > 0) {
/* Busy wait — in a real OS we'd use a proper sleep/wait */
asm volatile("pause");
}
if (need_sti) {
asm volatile("cli");
}
if (timeout == 0) {
offset_print(" FLOPPY: IRQ timeout\n");
return -1;
}
floppy_irq_received = 0;
return 0;
}
/**
* Write a byte to the FDC FIFO register.
* Waits for the controller to be ready (MSR RQM=1, DIO=0).
*
* @param val Byte to write.
* @return 0 on success, -1 on timeout.
*/
static int fdc_write_byte(uint8_t val) {
int timeout = 500000;
while (--timeout > 0) {
uint8_t msr = inb(FDC_MSR);
if ((msr & (MSR_RQM | MSR_DIO)) == MSR_RQM) {
outb(FDC_FIFO, val);
return 0;
}
}
offset_print(" FLOPPY: write timeout\n");
return -1;
}
/**
* Read a byte from the FDC FIFO register.
* Waits for the controller to have data ready (MSR RQM=1, DIO=1).
*
* @return Byte read, or -1 on timeout.
*/
static int fdc_read_byte(void) {
int timeout = 500000;
while (--timeout > 0) {
uint8_t msr = inb(FDC_MSR);
if ((msr & (MSR_RQM | MSR_DIO)) == (MSR_RQM | MSR_DIO)) {
return (int)inb(FDC_FIFO);
}
}
offset_print(" FLOPPY: read timeout\n");
return -1;
}
/**
* Issue a SENSE INTERRUPT command and read the result.
*
* @param st0_out Output: ST0 byte.
* @param cyl_out Output: current cylinder.
*/
static void fdc_sense_interrupt(uint8_t *st0_out, uint8_t *cyl_out) {
fdc_write_byte(FDC_CMD_SENSE_INT);
int st0 = fdc_read_byte();
int cyl = fdc_read_byte();
if (st0_out) *st0_out = (uint8_t)(st0 >= 0 ? st0 : 0xFF);
if (cyl_out) *cyl_out = (uint8_t)(cyl >= 0 ? cyl : 0xFF);
}
/* ================================================================
* Motor control
* ================================================================ */
/**
* Turn the motor on for a drive.
*
* @param drive Drive number (0 or 1).
*/
static void fdc_motor_on(uint8_t drive) {
if (drive >= FLOPPY_MAX_DRIVES) return;
if (floppy_drives[drive].motor_on) return;
uint8_t dor = drive | DOR_RESET | DOR_DMA_IRQ |
(DOR_MOTOR_A << drive);
outb(FDC_DOR, dor);
/* Wait ~500 ms for motor spin-up (busy wait in timer ticks) */
for (volatile int i = 0; i < 2000000; i++) {
asm volatile("pause");
}
floppy_drives[drive].motor_on = 1;
}
/**
* Turn the motor off for a drive.
*
* @param drive Drive number (0 or 1).
*/
static void fdc_motor_off(uint8_t drive) {
if (drive >= FLOPPY_MAX_DRIVES) return;
uint8_t dor = drive | DOR_RESET | DOR_DMA_IRQ;
outb(FDC_DOR, dor);
floppy_drives[drive].motor_on = 0;
}
/* ================================================================
* DMA setup for ISA DMA channel 2
* ================================================================ */
/**
* Set up ISA DMA channel 2 for a floppy transfer.
*
* The floppy controller uses DMA channel 2. We configure the DMA
* controller with the physical address of our bounce buffer and the
* transfer length.
*
* @param addr Physical address of the DMA buffer (must be < 16 MB).
* @param len Transfer length in bytes (minus 1 is written to count).
* @param read 1 for read (disk→memory), 0 for write (memory→disk).
*/
static void dma_setup(uint32_t addr, uint16_t len, int read) {
/* Mask DMA channel 2 */
outb(0x0A, 0x06);
/* Reset flip-flop */
outb(0x0C, 0xFF);
/* Mode: single transfer, auto-init disabled, increment, channel 2
* Read = 0x46 (single, addr incr, write-to-memory = FDC read)
* Write = 0x4A (single, addr incr, read-from-memory = FDC write) */
outb(0x0B, read ? 0x46 : 0x4A);
/* Address (low 16 bits via channel 2 address reg 0x04) */
outb(0x04, (uint8_t)(addr & 0xFF));
outb(0x04, (uint8_t)((addr >> 8) & 0xFF));
/* Page register for channel 2 (port 0x81) — bits 16-23 of address */
outb(0x81, (uint8_t)((addr >> 16) & 0xFF));
/* Count (len - 1, low byte first) */
outb(0x05, (uint8_t)((len - 1) & 0xFF));
outb(0x05, (uint8_t)(((len - 1) >> 8) & 0xFF));
/* Unmask DMA channel 2 */
outb(0x0A, 0x02);
}
/* ================================================================
* CHS conversion
* ================================================================ */
/**
* Convert an LBA address to CHS for a 1.44 MB floppy.
*
* @param lba Logical block address.
* @param cyl_out Output: cylinder number.
* @param head_out Output: head number.
* @param sec_out Output: sector number (1-based).
*/
static void lba_to_chs(uint32_t lba, uint8_t *cyl_out,
uint8_t *head_out, uint8_t *sec_out) {
*cyl_out = (uint8_t)(lba / (FLOPPY_HEADS * FLOPPY_SECTORS_PER_TRACK));
uint32_t tmp = lba % (FLOPPY_HEADS * FLOPPY_SECTORS_PER_TRACK);
*head_out = (uint8_t)(tmp / FLOPPY_SECTORS_PER_TRACK);
*sec_out = (uint8_t)((tmp % FLOPPY_SECTORS_PER_TRACK) + 1);
}
/* ================================================================
* Seek and calibrate
* ================================================================ */
/**
* Recalibrate a drive (seek to track 0).
*
* @param drive Drive number.
* @return 0 on success, -1 on failure.
*/
static int fdc_recalibrate(uint8_t drive) {
floppy_irq_received = 0;
fdc_write_byte(FDC_CMD_RECALIBRATE);
fdc_write_byte(drive);
if (fdc_wait_irq() != 0) return -1;
uint8_t st0, cyl;
fdc_sense_interrupt(&st0, &cyl);
if (cyl != 0) {
offset_print(" FLOPPY: recalibrate failed, cyl=");
print_hex(cyl);
offset_print("\n");
return -1;
}
floppy_drives[drive].current_track = 0;
return 0;
}
/**
* Seek to a specific cylinder.
*
* @param drive Drive number.
* @param cyl Target cylinder.
* @return 0 on success, -1 on failure.
*/
static int fdc_seek(uint8_t drive, uint8_t cyl) {
if (floppy_drives[drive].current_track == cyl) return 0;
floppy_irq_received = 0;
fdc_write_byte(FDC_CMD_SEEK);
fdc_write_byte((0 << 2) | drive); /* Head 0, drive N */
fdc_write_byte(cyl);
if (fdc_wait_irq() != 0) return -1;
uint8_t st0, res_cyl;
fdc_sense_interrupt(&st0, &res_cyl);
if (res_cyl != cyl) {
offset_print(" FLOPPY: seek failed, wanted cyl=");
print_hex(cyl);
offset_print(" got=");
print_hex(res_cyl);
offset_print("\n");
return -1;
}
floppy_drives[drive].current_track = cyl;
return 0;
}
/* ================================================================
* Read / Write sector
* ================================================================ */
/**
* Read a single sector from a floppy drive using DMA.
*
* @param drive Drive number (0 or 1).
* @param lba Logical sector address.
* @param buf Output buffer (512 bytes).
* @return 0 on success, -1 on failure.
*/
static int fdc_read_sector(uint8_t drive, uint32_t lba, void *buf) {
if (lba >= FLOPPY_TOTAL_SECTORS) return -1;
uint8_t cyl, head, sec;
lba_to_chs(lba, &cyl, &head, &sec);
fdc_motor_on(drive);
/* Seek to the correct cylinder */
if (fdc_seek(drive, cyl) != 0) {
fdc_motor_off(drive);
return -1;
}
/* Set up DMA for read (disk → memory) */
uint32_t dma_addr = (uint32_t)dma_buffer;
dma_setup(dma_addr, FLOPPY_SECTOR_SIZE, 1);
/* Issue READ DATA command */
floppy_irq_received = 0;
fdc_write_byte(FDC_CMD_READ_DATA);
fdc_write_byte((head << 2) | drive);
fdc_write_byte(cyl);
fdc_write_byte(head);
fdc_write_byte(sec);
fdc_write_byte(2); /* Sector size code: 2 = 512 bytes */
fdc_write_byte(FLOPPY_SECTORS_PER_TRACK); /* End of track */
fdc_write_byte(0x1B); /* GPL: gap length */
fdc_write_byte(0xFF); /* DTL: unused when sector size != 0 */
/* Wait for command completion */
if (fdc_wait_irq() != 0) {
fdc_motor_off(drive);
return -1;
}
/* Read result phase (7 bytes) */
uint8_t st0 = (uint8_t)fdc_read_byte();
uint8_t st1 = (uint8_t)fdc_read_byte();
uint8_t st2 = (uint8_t)fdc_read_byte();
(void)fdc_read_byte(); /* C */
(void)fdc_read_byte(); /* H */
(void)fdc_read_byte(); /* R */
(void)fdc_read_byte(); /* N */
/* Check for errors */
if ((st0 & 0xC0) != 0 || st1 != 0 || st2 != 0) {
offset_print(" FLOPPY: read error st0=");
print_hex(st0);
offset_print(" st1=");
print_hex(st1);
offset_print(" st2=");
print_hex(st2);
offset_print("\n");
fdc_motor_off(drive);
return -1;
}
/* Copy from DMA buffer to caller's buffer */
memcpy(buf, dma_buffer, FLOPPY_SECTOR_SIZE);
fdc_motor_off(drive);
return 0;
}
/**
* Write a single sector to a floppy drive using DMA.
*
* @param drive Drive number (0 or 1).
* @param lba Logical sector address.
* @param buf Input buffer (512 bytes).
* @return 0 on success, -1 on failure.
*/
static int fdc_write_sector(uint8_t drive, uint32_t lba, const void *buf) {
if (lba >= FLOPPY_TOTAL_SECTORS) return -1;
uint8_t cyl, head, sec;
lba_to_chs(lba, &cyl, &head, &sec);
fdc_motor_on(drive);
if (fdc_seek(drive, cyl) != 0) {
fdc_motor_off(drive);
return -1;
}
/* Copy data to DMA buffer */
memcpy(dma_buffer, buf, FLOPPY_SECTOR_SIZE);
/* Set up DMA for write (memory → disk) */
uint32_t dma_addr = (uint32_t)dma_buffer;
dma_setup(dma_addr, FLOPPY_SECTOR_SIZE, 0);
/* Issue WRITE DATA command */
floppy_irq_received = 0;
fdc_write_byte(FDC_CMD_WRITE_DATA);
fdc_write_byte((head << 2) | drive);
fdc_write_byte(cyl);
fdc_write_byte(head);
fdc_write_byte(sec);
fdc_write_byte(2); /* Sector size code: 512 bytes */
fdc_write_byte(FLOPPY_SECTORS_PER_TRACK);
fdc_write_byte(0x1B);
fdc_write_byte(0xFF);
if (fdc_wait_irq() != 0) {
fdc_motor_off(drive);
return -1;
}
/* Read result phase */
uint8_t st0 = (uint8_t)fdc_read_byte();
uint8_t st1 = (uint8_t)fdc_read_byte();
uint8_t st2 = (uint8_t)fdc_read_byte();
(void)fdc_read_byte();
(void)fdc_read_byte();
(void)fdc_read_byte();
(void)fdc_read_byte();
if ((st0 & 0xC0) != 0 || st1 != 0 || st2 != 0) {
offset_print(" FLOPPY: write error st0=");
print_hex(st0);
offset_print(" st1=");
print_hex(st1);
offset_print(" st2=");
print_hex(st2);
offset_print("\n");
fdc_motor_off(drive);
return -1;
}
fdc_motor_off(drive);
return 0;
}
/* ================================================================
* Devicefs block operations
* ================================================================ */
/**
* Read sectors from a floppy drive.
* dev_data is a pointer to the floppy_drive_t.
*/
static int floppy_block_read(void *dev_data, uint32_t lba,
uint32_t count, void *buf) {
floppy_drive_t *drv = (floppy_drive_t *)dev_data;
if (!drv || !drv->present) return -1;
uint8_t *p = (uint8_t *)buf;
for (uint32_t i = 0; i < count; i++) {
if (fdc_read_sector(drv->drive_num, lba + i, p) != 0) {
return -1;
}
p += FLOPPY_SECTOR_SIZE;
}
return 0;
}
/**
* Write sectors to a floppy drive.
*/
static int floppy_block_write(void *dev_data, uint32_t lba,
uint32_t count, const void *buf) {
floppy_drive_t *drv = (floppy_drive_t *)dev_data;
if (!drv || !drv->present) return -1;
const uint8_t *p = (const uint8_t *)buf;
for (uint32_t i = 0; i < count; i++) {
if (fdc_write_sector(drv->drive_num, lba + i, p) != 0) {
return -1;
}
p += FLOPPY_SECTOR_SIZE;
}
return 0;
}
/**
* Get floppy sector size (always 512).
*/
static uint32_t floppy_block_sector_size(void *dev_data) {
(void)dev_data;
return FLOPPY_SECTOR_SIZE;
}
/**
* Get total sector count for a 1.44 MB floppy.
*/
static uint32_t floppy_block_sector_count(void *dev_data) {
(void)dev_data;
return FLOPPY_TOTAL_SECTORS;
}
/** Block device operations for floppy drives. */
static devicefs_block_ops_t floppy_block_ops = {
.read_sectors = floppy_block_read,
.write_sectors = floppy_block_write,
.sector_size = floppy_block_sector_size,
.sector_count = floppy_block_sector_count,
};
/* ================================================================
* Controller initialization
* ================================================================ */
/**
* Reset the floppy disk controller.
*
* Toggles the RESET bit in the DOR and re-initializes the controller.
*
* @return 0 on success, -1 on failure.
*/
static int fdc_reset(void) {
/* Enter reset state */
outb(FDC_DOR, 0x00);
/* Small delay */
for (volatile int i = 0; i < 100000; i++) {
asm volatile("pause");
}
/* Exit reset: enable DMA/IRQ, select drive 0, motor off */
floppy_irq_received = 0;
outb(FDC_DOR, DOR_RESET | DOR_DMA_IRQ);
/* Controller generates IRQ 6 after reset */
if (fdc_wait_irq() != 0) {
offset_print(" FLOPPY: no IRQ after reset\n");
return -1;
}
/* Sense interrupt for each of the 4 possible drives */
for (int i = 0; i < 4; i++) {
uint8_t st0, cyl;
fdc_sense_interrupt(&st0, &cyl);
}
/* Set data rate to 500 kbps (for 1.44 MB HD floppies) */
outb(FDC_CCR, 0x00);
/* SPECIFY command: step rate = 3 ms, head unload = 240 ms,
* head load = 16 ms, non-DMA mode = 0 */
fdc_write_byte(FDC_CMD_SPECIFY);
fdc_write_byte(0xDF); /* SRT=3ms, HUT=240ms */
fdc_write_byte(0x02); /* HLT=16ms, NDMA=0 */
return 0;
}
/* ================================================================
* Driver framework integration
* ================================================================ */
/**
* Probe for floppy controller.
* Reads CMOS to determine if any floppy drives are configured.
*/
static driver_probe_result_t floppy_probe(void) {
uint8_t cmos_floppy = cmos_read(0x10);
uint8_t drive_a = (cmos_floppy >> 4) & 0x0F;
uint8_t drive_b = cmos_floppy & 0x0F;
if (drive_a == CMOS_FLOPPY_NONE && drive_b == CMOS_FLOPPY_NONE) {
offset_print(" FLOPPY: no drives in CMOS\n");
return DRIVER_PROBE_NOT_FOUND;
}
return DRIVER_PROBE_OK;
}
/**
* Initialize the floppy controller and register detected drives.
*/
static int floppy_driver_init(void) {
memset(floppy_drives, 0, sizeof(floppy_drives));
/* Read CMOS for drive types */
uint8_t cmos_floppy = cmos_read(0x10);
uint8_t types[2] = {
(cmos_floppy >> 4) & 0x0F,
cmos_floppy & 0x0F,
};
/* Unmask IRQ 6 in the PIC */
pic_clear_mask(6);
/* Reset the controller */
if (fdc_reset() != 0) {
offset_print(" FLOPPY: controller reset failed\n");
return -1;
}
/* Register each detected drive */
int found = 0;
for (int i = 0; i < 2; i++) {
if (types[i] == CMOS_FLOPPY_NONE) continue;
/* We only fully support 1.44 MB for now, but register others too */
floppy_drives[i].drive_num = (uint8_t)i;
floppy_drives[i].cmos_type = types[i];
floppy_drives[i].present = 1;
floppy_drives[i].motor_on = 0;
floppy_drives[i].current_track = 0xFF; /* Unknown */
const char *type_str;
switch (types[i]) {
case CMOS_FLOPPY_360K: type_str = "360K 5.25\""; break;
case CMOS_FLOPPY_1200K: type_str = "1.2M 5.25\""; break;
case CMOS_FLOPPY_720K: type_str = "720K 3.5\""; break;
case CMOS_FLOPPY_1440K: type_str = "1.44M 3.5\""; break;
case CMOS_FLOPPY_2880K: type_str = "2.88M 3.5\""; break;
default: type_str = "unknown"; break;
}
offset_print(" FLOPPY: drive ");
print_hex(i);
offset_print(": ");
offset_print(type_str);
offset_print("\n");
/* Recalibrate the drive */
fdc_motor_on((uint8_t)i);
fdc_recalibrate((uint8_t)i);
fdc_motor_off((uint8_t)i);
/* Register with devicefs */
devicefs_register_block("floppy", &floppy_block_ops,
&floppy_drives[i]);
found++;
}
if (found == 0) {
offset_print(" FLOPPY: no usable drives found\n");
} else {
offset_print(" FLOPPY: ");
print_hex((uint32_t)found);
offset_print(" drive(s) initialized\n");
}
return 0;
}
/* ================================================================
* IRQ handler
* ================================================================ */
void floppy_irq(void) {
floppy_irq_received = 1;
}
/* ================================================================
* Public API & driver registration
* ================================================================ */
int floppy_init(void) {
return floppy_driver_init();
}
/** Driver descriptor. */
static const driver_t floppy_driver = {
.name = "floppy",
.probe = floppy_probe,
.init = floppy_driver_init,
};
REGISTER_DRIVER(floppy_driver);

122
src/floppy.h Normal file
View File

@@ -0,0 +1,122 @@
/**
* @file floppy.h
* @brief Floppy disk controller driver.
*
* Drives the Intel 82077AA-compatible floppy disk controller.
* Detects floppy drives via CMOS and registers them with the devicefs
* subsystem as block devices with class "floppy" (floppy1, floppy2, ...).
*
* Supports 1.44 MB 3.5" HD floppies (18 sectors/track, 80 tracks, 2 heads).
* Uses IRQ 6 for command completion notification and DMA channel 2 for
* data transfer.
*/
#ifndef FLOPPY_H
#define FLOPPY_H
#include <stdint.h>
/* ================================================================
* Floppy controller I/O port registers (primary controller)
* ================================================================ */
#define FDC_BASE 0x3F0 /**< Base I/O port of primary FDC. */
#define FDC_SRA 0x3F0 /**< Status Register A (read-only). */
#define FDC_SRB 0x3F1 /**< Status Register B (read-only). */
#define FDC_DOR 0x3F2 /**< Digital Output Register. */
#define FDC_TDR 0x3F3 /**< Tape Drive Register. */
#define FDC_MSR 0x3F4 /**< Main Status Register (read-only). */
#define FDC_DSR 0x3F4 /**< Data Rate Select Register (write-only). */
#define FDC_FIFO 0x3F5 /**< Data (FIFO) register. */
#define FDC_DIR 0x3F7 /**< Digital Input Register (read-only). */
#define FDC_CCR 0x3F7 /**< Configuration Control Register (write-only). */
/* ================================================================
* Digital Output Register (DOR) bits
* ================================================================ */
#define DOR_DRIVE_SEL_MASK 0x03 /**< Drive select (0-3). */
#define DOR_RESET 0x04 /**< 0 = enter reset, 1 = normal operation. */
#define DOR_DMA_IRQ 0x08 /**< 1 = enable DMA and IRQ. */
#define DOR_MOTOR_A 0x10 /**< Motor enable for drive 0. */
#define DOR_MOTOR_B 0x20 /**< Motor enable for drive 1. */
#define DOR_MOTOR_C 0x40 /**< Motor enable for drive 2. */
#define DOR_MOTOR_D 0x80 /**< Motor enable for drive 3. */
/* ================================================================
* Main Status Register (MSR) bits
* ================================================================ */
#define MSR_ACTA 0x01 /**< Drive 0 is seeking. */
#define MSR_ACTB 0x02 /**< Drive 1 is seeking. */
#define MSR_ACTC 0x04 /**< Drive 2 is seeking. */
#define MSR_ACTD 0x08 /**< Drive 3 is seeking. */
#define MSR_CB 0x10 /**< Command busy. */
#define MSR_NDMA 0x20 /**< Non-DMA mode. */
#define MSR_DIO 0x40 /**< Data direction: 1 = FDC→CPU, 0 = CPU→FDC. */
#define MSR_RQM 0x80 /**< Ready for data transfer. */
/* ================================================================
* Floppy commands
* ================================================================ */
#define FDC_CMD_SPECIFY 0x03 /**< Specify step rate, head load/unload. */
#define FDC_CMD_WRITE_DATA 0xC5 /**< Write Data (MT+MFM). */
#define FDC_CMD_READ_DATA 0xE6 /**< Read Data (MT+MFM+SK). */
#define FDC_CMD_RECALIBRATE 0x07 /**< Recalibrate (seek to track 0). */
#define FDC_CMD_SENSE_INT 0x08 /**< Sense Interrupt Status. */
#define FDC_CMD_SEEK 0x0F /**< Seek to a track. */
#define FDC_CMD_VERSION 0x10 /**< Get controller version. */
#define FDC_CMD_CONFIGURE 0x13 /**< Configure controller. */
#define FDC_CMD_LOCK 0x94 /**< Lock configuration. */
/* ================================================================
* Geometry for 1.44 MB 3.5" HD floppy
* ================================================================ */
#define FLOPPY_SECTORS_PER_TRACK 18 /**< Sectors per track. */
#define FLOPPY_HEADS 2 /**< Number of heads. */
#define FLOPPY_TRACKS 80 /**< Number of cylinders/tracks. */
#define FLOPPY_SECTOR_SIZE 512 /**< Bytes per sector. */
#define FLOPPY_TOTAL_SECTORS (FLOPPY_SECTORS_PER_TRACK * FLOPPY_HEADS * FLOPPY_TRACKS)
/* ================================================================
* CMOS floppy type codes
* ================================================================ */
#define CMOS_FLOPPY_NONE 0x00 /**< No drive. */
#define CMOS_FLOPPY_360K 0x01 /**< 5.25" 360 KB. */
#define CMOS_FLOPPY_1200K 0x02 /**< 5.25" 1.2 MB. */
#define CMOS_FLOPPY_720K 0x03 /**< 3.5" 720 KB. */
#define CMOS_FLOPPY_1440K 0x04 /**< 3.5" 1.44 MB. */
#define CMOS_FLOPPY_2880K 0x05 /**< 3.5" 2.88 MB. */
/** Maximum supported floppy drives. */
#define FLOPPY_MAX_DRIVES 2
/**
* Per-drive state.
*/
typedef struct floppy_drive {
uint8_t drive_num; /**< Drive number (0 or 1). */
uint8_t cmos_type; /**< CMOS type code. */
uint8_t present; /**< 1 if drive is detected and usable. */
uint8_t motor_on; /**< 1 if motor is currently running. */
uint8_t current_track; /**< Last sought cylinder (cached). */
} floppy_drive_t;
/**
* Initialize the floppy disk controller driver.
* Detects drives via CMOS and registers them with devicefs.
*
* @return 0 on success, non-zero on failure.
*/
int floppy_init(void);
/**
* IRQ 6 handler — called from isr_handler when vector 38 fires.
*/
void floppy_irq(void);
#endif /* FLOPPY_H */

View File

@@ -3,6 +3,7 @@
#include "process.h"
#include "syscall.h"
#include "keyboard.h"
#include "floppy.h"
#include <stdint.h>
/* Forward declaration for kernel panic or similar */
@@ -64,6 +65,9 @@ void isr_handler(registers_t *regs)
} else if (regs->int_no == 33) {
/* Keyboard IRQ */
keyboard_irq(regs);
} else if (regs->int_no == 38) {
/* Floppy IRQ */
floppy_irq();
}
return;
}