Attempt 2 #2
@@ -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
43
build.log
Normal 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
|
||||
@@ -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
688
src/floppy.c
Normal 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
122
src/floppy.h
Normal 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 */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user