📄 oofdc.cxx
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* Low-level floppy controller implementation. * FIX - don't start the motor shutdown timer until the operation * queue is empty! */#include <kernel/Diag.hxx>#include "kernel.hxx"#include "lostart.hxx"#include <kernel/io.h>#include "IDT.hxx"#include "CMOS.hxx"#include "KernThread.hxx"#include "CoreObject.hxx"#include "IoReq.hxx"#include "DMA.hxx"#include "UserMem.hxx"#include "SysConfig.hxx"#include "SysTimer.hxx"#include "DiskCtrlr.hxx"#include "DiskVolume.hxx"/* #define FDCDEBUG * #define FDCDEBUG2 * #define PARANOID * #define FDCMOTOR */struct FloppyInfo { uint16_t totSectors; uint8_t nSec; uint8_t nHd; uint8_t nCyl; uint8_t doubleStep; uint8_t gap; uint8_t dataRate; uint8_t spec1; uint8_t fmt_gap; char * name; uint8_t nextToTry;};/* Formatting parameters for various media in various drives. * Note that the order of these is chosen so that the first five * entries match the drive type encodings returned by the CMOS NVRAM. */static FloppyInfo FloppyParams[] = { { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,"None", 0 }, /* no testing */ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k", 0 }, /* 360kB PC diskettes */ /* Following entry used to have '6' in its nextToTry slot: */ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M", 0 }, /* 1.2 MB AT-diskettes */ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720K", 0 }, /* 720kB diskette */ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M", 3 }, /* 1.44MB diskette */#if 0 /* we do not support these ancient pieces of crap. Actually, we * only support 360 kb and 720 kb because it's easier than * adjusting what the BIOS tells us. */ { 720, 9,2,40,1,0x2A,0x02,0xDF,0x50,NULL, 0 }, /* 360kB in 720kB drive */ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,NULL, 0 }, /* 360kB in 1.2MB drive */ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,NULL, 0 }, /* 720kB in 1.2MB drive */#endif};#define NUM_UNITS 2class FDC : public DiskCtrlr { /* Ports on the NEC 765 controller: */ struct NECREG { enum { DOR = 0x2, /* digital output register */ TDR = 0x3, /* tape drive register (not on NEC 765) */ STATUS = 0x4, /* status register */ DATA = 0x5, /* data register */ DIR = 0x7, /* digital input register: read only */ DCR = 0x7, /* Diskette control register: write only */ }; }; /* bits in the DOR register: */ struct DOR { enum { Motor3 = 0x80, /* set if indicated motor is spinning */ Motor2 = 0x40, Motor1 = 0x20, Motor0 = 0x10, DmaGate = 0x08, /* set for most commands */ Reset = 0x04, /* set for most commands */ DrvSel1 = 0x02, DrvSel0 = 0x01, }; }; struct STATUS { enum { Master = 0x80, /* host can send if set to 1 */ ReqRead = 0x40, /* 1 indicates read */ NonDMA = 0x20, /* set in specify command */ CmdBusy = 0x10, /* 1 if command in progress */ Drv3Busy = 0x08, Drv2Busy = 0x04, Drv1Busy = 0x02, Drv0Busy = 0x01, }; }; /* NEC Commands that we use: */ struct NECCMD { enum { VERSION = 0x10, CONFIGURE = 0x13, SPECIFY = 0x03, RECALIBRATE = 0x07, SENSEI = 0x08, SEEK = 0x0f, /* Floppy I/O operations */ READ = 0xc6, /* with MT, MFM. 0xE6 with skip deleted */ WRITE = 0xc5, /* with MT, MFM */ FORMAT = 0x4d, /* with MFM */ }; }; /* Masks and values for Status Register 0: */ struct SR0 { enum { IC = 0xC0, /* interrupt code mask */ ICnormal = 0x00, /* normal completion */ ICabnormal = 0x40, /* failed */ ICbadcmd = 0x80, /* bad command */ ICpolled = 0xC0, /* interrupted by a poll operation. */ SeekEnd = 0x20, /* Seek completed */ EquipChk = 0x10, /* Equipment Check - 1 if recalibrate */ /* did not make it to track 0 or a * relative seek overran track 0 */ Head = 0x04, /* Head address mask */ Head0 = 0x00, Head1 = 0x04, DrvSel = 0x03, /* Drive select mask. Values are 0..3 */ }; }; struct SR1 { enum { CylEnd = 0x80, /* Hit end of cylinder */ BadCrc = 0x20, /* CRC on data or ID failed */ Overrun = 0x10, /* DMA service too slow */ NoData = 0x04, /* sector may have been deleted */ WriteProt = 0x02, /* Write Protected */ NoAddr = 0x01, /* Missing address mark */ }; }; protected: uint8_t dor; /* controller state holders: */ int nresults; /* number of results from last operation */ uint8_t results[8]; /* actual result values */ void Reset(); void OutFDC(uint8_t); int GetResults(); void ConfigureFDC(); FloppyInfo *curParams; /* How FDC is currently programmed */ /* UNIT OPERATIONS * * These are here so that only one new class needs to be defined for * a new controller type. The unit and the controller are * incestuously related anyway. */ DiskUnit* units[NUM_UNITS]; DiskUnit& GetUnit(uint8_t whichUnit); void StartIO(uint8_t unit); void StartMount(uint8_t unit); bool CanSchedule(uint8_t unit); void OnEvent(Event& event); void OnKeyCall(); void OnEvent(uint8_t unit, Event& event); bool Probe(uint8_t unit); void Attach(uint8_t unit); void OnKeyCall(uint8_t unit);public: FDC(AutoConf::ID id, io_t ioaddr); virtual void Probe(); virtual void Attach();};FDC fdca(AutoConf::FDCA, (io_t) 0x3f0);DiskUnit fdca0(AutoConf::FDCA0, &fdca, 0);DiskUnit fdca1(AutoConf::FDCA1, &fdca, 1);DiskUnit fdca2(AutoConf::FDCA2, &fdca, 2);DiskUnit fdca3(AutoConf::FDCA3, &fdca, 3);#if 0/* G++ is broken - it doesn't support this. */DiskUnit* FDCAunits[4] = { { AutoConf::FDCA0, fdca, 0 }, { AutoConf::FDCA1, fdca, 1 }, { AutoConf::FDCA2, fdca, 2 }, { AutoConf::FDCA3, fdca, 3 }};#endifFDC::FDC(AutoConf::ID id, io_t ioaddr): DiskCtrlr(id, NUM_UNITS, ioaddr, (kva_t)0, McMem1M){ int i; dor = 0; if (id == AutoConf::FDCA) { units[0] = &fdca0; units[1] = &fdca1;#if (NUM_UNITS > 2) units[2] = &fdca2;#endif#if (NUM_UNITS > 3) units[3] = &fdca3;#endif }}voidFDC::Probe(){ present = CMOS::HaveFDC();}voidFDC::Attach(){ IDT::RegisterHandler(IntFloppy, this); Reset();}boolFDC::Probe(uint8_t unit){ DiskUnit& du = GetUnit(unit); uint32_t type = CMOS::fdType(unit); du.type = type; if (du.type) { du.isRemovable = true; du.bootAttach = (unit == SysConfig.boot.drive) ? true : false; du.multiFormat = true; Diag::printf("%s: %s\n", du.Name(), FloppyParams[type].name); } return (du.type != 0);}voidFDC::Attach(uint8_t unit){ /* FIX: this needs to change! */ if (present) attached = true;}voidFDC::Reset(){ dor = 0; /* Reset FDC, Motors off */ outb(NECREG::DOR, dor); Stall(100); state = CsResetWait; dor = DOR::Reset|DOR::DmaGate; /* re-enable the part */ outb(ioBase + NECREG::DOR, dor); while (state == CsResetWait) Stall(100); assert(state == CsActive);}voidFDC::OnEvent(Event& e){ switch(state) { case CsActive: OnEvent(curUnit, e); return; case CsResetWait: { assert(e.kind == Event::Interrupt); curParams = 0; curUnit = 0; /* Controller comes back in polling mode. Must run four SENSEI * operations to clear this mode. */ int i; for (i = 0; i < NUM_UNITS; i++) { DiskUnit& du = GetUnit(i); /* sense the drive status */ OutFDC(NECCMD::SENSEI); if (GetResults() != 2) Diag::fatal(0,"Improper results from FDC reset\n"); /* The unit is now spun down. */ du.ioState = IosSpunDown; } state = CsActive; KernThread::SetReady(Thread::DiskDaemon); return; } }}voidFDC::OnEvent(uint8_t unit, Event& e){ DiskUnit& du = GetUnit(unit); if (e.kind == Event::Interrupt) { switch(du.ioState) { case IosRecalWait: du.ioState = IosRecalDone; break; case IosSeekWait: du.ioState = IosSeekDone; break; case IosReadWait: du.ioState = IosReadDone; break; case IosWriteWait: du.ioState = IosWriteDone; break; case IosFormatWait: du.ioState = IosFormatDone; break; default: Diag::fatal(0, "%s: Unexpected interrupt\n", Name()); } KernThread::SetReady(Thread::DiskDaemon); } if (e.kind == Event::Timer) { switch(du.ioState) { case IosSpinUpWait: du.ioState = IosNeedRecal; KernThread::SetReady(Thread::DiskDaemon); break; case IosSpinDownWait: dor &= ~(DOR::Motor0 << unit); outb(NECREG::DOR, dor); du.ioState = IosSpunDown; break; default: Diag::fatal(0, "%s: Unexpected watchdog timer\n", Name()); } }}boolFDC::CanSchedule(uint8_t unit){ DiskUnit& du = GetUnit(unit); /* If we have nothing to do, pass the token. Note that the effect of * this is that the token runs just ahead of the unit index in * DiskCtrlr::StartIO(). */ if (!du.GetNextRequest()) { curUnit ++; if (curUnit == nUnits) curUnit -= nUnits; return false; } /* If we have something to do, we can start it if we have the token * or if we need to start the motor. */ if (unit == curUnit) return true; if (du.ioState == DiskCtrlr::IosSpunDown) return true; return false;}voidFDC::OnKeyCall(){ Diag::fatal(0, "FDC::OnKeyCall called\n");}voidFDC::OnKeyCall(uint8_t unit){ Diag::fatal(0, "FDC::OnKeyCall(%d) called\n", unit);}DiskUnit& FDC::GetUnit(uint8_t whichUnit){ assert(whichUnit < NUM_UNITS); return *(units[whichUnit]);}voidFDC::OutFDC(uint8_t b){#ifdef FDCIODEBUG Diag::printf("FDC::OutFDC(%x)\n", b);#endif uint8_t r; for (int i = 0; i < 10000; i++) { r = inb(NECREG::STATUS); r &= (STATUS::Master | STATUS::ReqRead); if (r == STATUS::Master) { outb(NECREG::DATA, b); return; } } Diag::fatal(0,"FDC::OutFDC() failed. Status Reg = %x\n", r);}intFDC::GetResults(){ int i, n = 0; for (i = 0 ; i < 1000 ; i++) { uint8_t r = inb(NECREG::STATUS); r &= (STATUS::Master|STATUS::ReqRead|STATUS::CmdBusy); if (r == STATUS::Master) { /* not busy, ready for commands */ return n; } if (r == (STATUS::Master|STATUS::ReqRead|STATUS::CmdBusy)) { if (n >= 8) { Diag::fatal(0,"FDC::GetResults(): too many answers\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -