📄 floppy.c~
字号:
/* * My feeble attempt at a floppy driver * Copyright (c) 2003, David H. Hovemeyer <daveho@cs.umd.edu> * $Revision: 1.22 $ * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "COPYING". */#include <geekos/screen.h>#include <geekos/string.h>#include <geekos/mem.h>#include <geekos/malloc.h>#include <geekos/int.h>#include <geekos/irq.h>#include <geekos/dma.h>#include <geekos/io.h>#include <geekos/timer.h>#include <geekos/kthread.h>#include <geekos/blockdev.h>#include <geekos/floppy.h>/* * NOTES: * The primary hardware target for this driver is Bochs 2.0. * Hopefully it will work on real hardware as well. *//* * Information sources * - Jeff's original GeekOS floppy driver * - http://debs.future.easyspace.com/Programming/Hardware/FDC/floppy.html * - Intel 82077AA data sheet: * http://www.nondot.org/sabre/os/files/Disk/82077AA_FloppyControllerDatasheet.pdf * - Frank van Gilluwe, _The Undocumented PC_, ISBN 0-201-47950-8, Chapter 10 *//* * TODO: * - Resetting the controller probably shouldn't require calibration, * since there is no guarantee a disk will be in the drive. * - Need to really support multiple drives. *//* * History: * 23-Oct-2003: Works under Bochs 2.0 for read transfers. * 12-Nov-2003: Modified to use block device API. *//* ---------------------------------------------------------------------- * Definitions * ---------------------------------------------------------------------- *//* * Floppy controller IRQ */#define FDC_IRQ 6/* * Floppy DMA channel */#define FDC_DMA 2/* * I/O ports */#define FDC_BASE 0x3F0#define FDC_DOR_REG 0x3F2 /*!< Digital output register (write) */#define FDC_STATUS_REG 0x3F4 /*!< Main status regiser (read) */#define FDC_DATA_RATE_SELECT_REG 0x3F4 /*!< Data rate select register (write) */#define FDC_DATA_REG 0x3F5 /*!< Data register (read, write) *//* * Status register */#define FDC_STATUS_MRQ (1 << 7)#define FDC_STATUS_DIO (1 << 6)#define FDC_STATUS_NDMA (1 << 5)#define FDC_STATUS_BUSY (1 << 4)#define FDC_STATUS_ACTIVE(drive) (1 << (drive))#define FDC_STATUS_READY_MASK (FDC_STATUS_MRQ | FDC_STATUS_DIO)#define FDC_STATUS_READY_WRITE FDC_STATUS_MRQ#define FDC_STATUS_READY_READ (FDC_STATUS_MRQ | FDC_STATUS_DIO)/* * DOR (Digital Output Register) bits */#define FDC_DOR_MOTOR(drive) (1 << ((drive) + 4))#define FDC_DOR_DMA_ENABLE (1 << 3)#define FDC_DOR_RESET_DISABLE (1 << 2)#define FDC_DOR_DRIVE_SELECT(drive) ((drive) & 0x3)/* * Commands */#define FDC_COMMAND_CALIBRATE 0x07#define FDC_COMMAND_SENSE_INT_STATUS 0x08#define FDC_COMMAND_SEEK 0x0F#define FDC_COMMAND_WRITE_SECTOR 0x05#define FDC_COMMAND_READ_SECTOR 0x06/* * Command bits */#define FDC_MULTI_TRACK 0x80#define FDC_MFM 0x40#define FDC_SKIP_DELETED 0x20/* * Status registers (from result phase) */#define FDC_ST0_SEEK_END (1 << 5)#define FDC_ST0_IS_SUCCESS(code) ((((code) >> 6) & 0x3) == 0)/* * CMOS configuration * See: http://www.osdever.net/tutorials/detecting_floppy_drives.php */#define CMOS_OUT 0x70#define CMOS_IN 0x71#define CMOS_FLOPPY_INDEX 0x10enum { FLOPPY_READ, FLOPPY_WRITE };/*#define FLOPPY_DEBUG */#ifdef FLOPPY_DEBUG# define Debug(args...) Print(args)#else# define Debug(args...)#endif/* ---------------------------------------------------------------------- * Variables * ---------------------------------------------------------------------- *//* * Floppy drive parameters. */struct Floppy_Parameters { int cylinders; int heads; int sectors; int sectorSizeCode; int gapLengthCode;};#define INVALID_FLOPPY_TYPE { -1, -1, -1, -1, -1 }/* * Parameters of known floppy types. * These are indexed by the floppy type reported in the CMOS. */static struct Floppy_Parameters s_floppyParamsTable[] = { INVALID_FLOPPY_TYPE, /* not installed */ INVALID_FLOPPY_TYPE, /* TODO: 360K 5.25" */ INVALID_FLOPPY_TYPE, /* TODO: 1.2M 5.25" */ INVALID_FLOPPY_TYPE, /* TODO: 720K 3.5" */ {80, 2, 18, 0x02, 0x1C}, /* 1.44M 3.5" */ INVALID_FLOPPY_TYPE, /* TODO: 2.88M 3.5" */};#define NUM_FLOPPY_TYPES (sizeof(s_floppyParamsTable) / sizeof(struct Floppy_Parameters))#define IS_VALID_FLOPPY_TYPE(type) \ ((type) < NUM_FLOPPY_TYPES && s_floppyParamsTable[(type)].cylinders > 0)/* * Parameters and state information about a floppy drive. */struct Floppy_Drive { struct Floppy_Parameters *params;};/* * Parameters and state information about installed drives. */struct Floppy_Drive s_driveTable[2];/* * Flag to indicate that a floppy interrupt occurred. */static volatile int s_interruptOccurred;/* * Page of memory used for floppy DMA. */static uchar_t *s_transferBuf;/* * Queue of floppy block I/O requests. */static struct Block_Request_List s_floppyRequestQueue;/* * Thread queue where request processing thread sleeps waiting for * a request to arrive. */static struct Thread_Queue s_floppyWaitQueue;/* ---------------------------------------------------------------------- * Private functions * ---------------------------------------------------------------------- *//* * Implementation of Open for floppy driver. */static int Floppy_Open(struct Block_Device *dev){ KASSERT(!dev->inUse); return 0;}/* * Implementation of Close for floppy driver. */static int Floppy_Close(struct Block_Device *dev){ KASSERT(dev->inUse); return 0;}/* * Implementation of Get_Num_Blocks for floppy driver. */static int Floppy_Get_Num_Blocks(struct Block_Device *dev){ struct Floppy_Drive *drive; struct Floppy_Parameters *params = drive->params; KASSERT(dev->unit >= 0 && dev->unit <= 1); drive = &s_driveTable[dev->unit]; params = drive->params; KASSERT(params != 0); return params->cylinders * params->heads * params->sectors;}/* * Block_Device_Ops for floppy driver. */static struct Block_Device_Ops s_floppyDeviceOps = { Floppy_Open, Floppy_Close, Floppy_Get_Num_Blocks,};/* * Interrupt handler. * The floppy controller generally issues an interrupt * to notify the driver of the completion of a command. *//* * For now, we just set a flag that the driver * can busy-wait for. */static void Floppy_Interrupt_Handler(struct Interrupt_State* state){ Begin_IRQ(state); Debug("Floppy_Interrupt_Handler!\n"); s_interruptOccurred = 1; End_IRQ(state);}/* * Initialize drive parameters based on the floppy type returned * by the CMOS. */static void Setup_Drive_Parameters(int drive, int type){ if (IS_VALID_FLOPPY_TYPE(type)) { struct Floppy_Parameters* params = &s_floppyParamsTable[type]; char devname[BLOCKDEV_MAX_NAME_LEN+1]; int rc; snprintf(devname, sizeof(devname), "fd%d", drive); Print(" %s: cyl=%d, heads=%d, sectors=%d\n", devname, params->cylinders, params->heads, params->sectors); s_driveTable[drive].params = params; /* Register the block device. */ rc = Register_Block_Device(devname, &s_floppyDeviceOps, drive, 0, &s_floppyWaitQueue, &s_floppyRequestQueue); if (rc != 0) Print(" Error: could not create block device for %s\n", devname); }}/* * Convert LBA address to CHS. */static void LBA_To_CHS(struct Floppy_Drive* drive, int lba, int *cylinder, int *head, int *sector){ struct Floppy_Parameters* params = drive->params; KASSERT(params != 0); *cylinder = lba / (params->heads * params->sectors); *head = (lba / params->sectors) % params->heads; *sector = (lba % params->sectors) + 1; KASSERT(*cylinder >= 0 && *cylinder < params->cylinders); KASSERT(*head >= 0 && *head < params->heads); KASSERT(*sector > 0 && *sector <= params->sectors);}/* * Wait for the MRQ bit to be set in the main status register. * This indicates that the controller is ready to accept data * or send data via the data register. */static void Wait_For_MRQ(uchar_t readyValue){ KASSERT(readyValue == FDC_STATUS_READY_READ || readyValue == FDC_STATUS_READY_WRITE); /* Wait for MRQ bit to be set in main status register */ while ((In_Byte(FDC_STATUS_REG) & FDC_STATUS_READY_MASK) != readyValue) ; /*Debug("Ready to accept command!\n"); */}/* * Get a byte from the data register. */static uchar_t Floppy_In(void){ Wait_For_MRQ(FDC_STATUS_READY_READ); return In_Byte(FDC_DATA_REG);}/* * Write a byte to the data register.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -