📄 ide_ide.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. */#include <kerninc/kernel.hxx>#include <kerninc/MsgLog.hxx>#include <kerninc/AutoConf.hxx>#include <kerninc/IoRegion.hxx>#include <kerninc/IntAction.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/BlockDev.hxx>#include <kerninc/IoRequest.hxx>#include <eros/Device.h>#include <arch/i486/kernel/SysConfig.hxx>#include <arch/i486/kernel/CMOS.hxx>static bool Probe(AutoConf *ac);static bool Attach(AutoConf *ac);struct Driver ac_ide = { "hd", Probe, Attach} ;#include "io.h"/* #define IDE_DEBUG *//* Driver for IDE interface devices and (also) older ST506 devices. * According to the ATA Faq, these interfaces are assigned as follows: * (REF: ATA Faq) * * Interface CS0-decode CS1-decode IRQ * 1 0x1F0-0x1F7 0x3F6-0x3F7 14 * 2 0x170-0x177 0x376-0x377 15 or 10 -- usually 15 * 3 0x1E8-0x1EF 0x3EE-0x3EF 12 or 11 * 4 0x168-0x16F 0x36E-0x36F 10 or 9 * * The current implementation does not configure interfaces 3 and 4, * because there is no real standard for port assignments on * interfaces 3 and 4, and I am reluctant to deploy what I cannot * test. */const uint16_t ide_cs0[] = { 0x1F0, 0x170, 0x1E8, 0x168 };const uint16_t ide_irq[] = { 14, 15, 12, 10 };#include "ide_ide.hxx"const char *ide_name[MAX_HWIF] = { "ide0", "ide1" };#include "ide_drive.hxx"#include "ide_hwif.hxx"#include "ide_group.hxx" #define BIOS_HD_BASE 0x1F0#define OK_TO_RESET_CONTROLLER 0/* In theory, these should not be statically allocated, but it * complexifies things to do otherwise, and they don't really take up * all that much space. */ide_group group_tbl[MAX_HWIF];ide_hwif hwif_tbl[MAX_HWIF];ide_hwif::ide_hwif(){ uint32_t hwifno = this - hwif_tbl; ndx = hwifno; irq = 0; /* probe for this */ ioBase = ide_cs0[hwifno]; /* ctlBase = ide_cs1[hwifno]; */ chipset = IDE::cs_unknown; noprobe = (hwifno > 1) ? true : false; present = false; /* Second unit may be present even if first unit isn't, so... */ nUnits = MAX_DRIVE; devClass = DEV_DISK_IDE; name = "ide"; group = &group_tbl[hwifno]; group->hwif[0] = this;#if 0 /* Given the number of bone-headed IDE controller chips out there, * and the fact that more are being discovered daily, proceed on the * assumption that the IDE interface chip is brain damaged until * proven otherwise. */ serialized = true;#endif int_handler = 0; dma_handler = 0; for (int drv = 0; drv < MAX_DRIVE; drv++) { drive[drv].ndx = drv; drive[drv].hwif = this; drive[drv].select.all = (drv << 4) | 0xa0; drive[drv].name[0] = 'h'; drive[drv].name[1] = 'd'; drive[drv].name[2] = 'a' + hwifno; drive[drv].name[3] = '0' + drv; drive[drv].name[4] = 0; }}voidide_hwif::SetHandler(uint8_t unit, bool (ide_hwif::*handler)()){ assert (unit == cur_drive); assert(int_handler == 0); int_handler = handler;#ifdef IDE_DEBUG MsgLog::printf("Set int handler to 0x%08x\n", &handler);#endif}/* Issue a simple drive command */voidide_hwif::DoCmd(uint8_t unit, uint8_t cmd, uint8_t nsec, bool (ide_hwif::*handler)()){ SetHandler(unit, handler); Put8(IDE_CTL_ALTSTATUS, drive[unit].ctl); Put8(IDE_NSECS, nsec); Put8(IDE_CMD, cmd);}/* * ide_do_reset() is the entry point to the drive/interface reset code. */voidide_hwif::DoReset(uint8_t unit){ DoReset1(unit, false);#ifdef CONFIG_BLK_DEV_IDETAPE if (drive->media == ide_tape) drive->tape.reset_issued=1;#endif /* CONFIG_BLK_DEV_IDETAPE */}/* * do_reset1() attempts to recover a confused drive by resetting it. * Unfortunately, resetting a disk drive actually resets all devices on * the same interface, so it can really be thought of as resetting the * interface rather than resetting the drive. * * ATAPI devices have their own reset mechanism which allows them to be * individually reset without clobbering other devices on the same interface. * * Unfortunately, the IDE interface does not generate an interrupt to let * us know when the reset operation has finished, so we must poll for this. * Equally poor, though, is the fact that this may a very long time to complete, * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, * we set a timer to poll at 50ms intervals. */voidide_hwif::DoReset1(uint8_t unit, bool /* suppressAtapi */){#ifdef CONFIG_BLK_DEV_IDEATAPI /* Interrupts may need to be disabled here... */ /* For an ATAPI device, first try an ATAPI SRST. */ if (drive[unit].media != IDE::med_disk) { MsgLog::fatal("Reset1 on non-disk not implemented\n"); if (!suppressAtapi) { if (!keep_settings) unmask = 0; SelectDrive(unit); Machine::SpinWait(20); Put8(IDE_CMD, WIN_SRST);#if 0 hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);#endif return; } }#endif /* CONFIG_BLK_DEV_IDEATAPI */ /* * First, reset any device state data we were maintaining * for any of the drives on this interface. */ for (unit = 0; unit < MAX_DRIVE; ++unit) { ide_drive *pUnit = &drive[unit]; pUnit->flags.all = 0; pUnit->flags.b.setGeom = 1; pUnit->flags.b.recal = 1; if (OK_TO_RESET_CONTROLLER) pUnit->multCount = 0;#if 0 if (!pUnit->keep_settings) { pUnit->mult_req = 0; pUnit->unmask = 0; if (pUnit->using_dma) { pUnit->using_dma = 0; MsgLog::printf("%s: disabled DMA\n", pUnit->name); } }#endif#if 0 if (pUnit->mult_req != pUnit->mult_count) pUnit->special.b.set_multmode = 1;#endif }#if OK_TO_RESET_CONTROLLER /* * Note that we also set nIEN while resetting the device, * to mask unwanted interrupts from the interface during the reset. * However, due to the design of PC hardware, this will cause an * immediate interrupt due to the edge transition it produces. * This single interrupt gives us a "fast poll" for drives that * recover from reset very quickly, saving us the first 50ms wait time. */ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */ udelay(5); /* more than enough time */ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; ide_set_handler (drive, &reset_pollfunc, HZ/20);#endif /* OK_TO_RESET_CONTROLLER */}voidide_hwif::SelectDrive(uint32_t unit){ Put8(IDE_DRV_HD, drive[unit].select.all);}/* * FUNCTIONS FOR THE EROS BLOCK DEVICE INTERFACE * */voidide_hwif::MountUnit(uint8_t unit){ assert (unit < MAX_DRIVE); assert (drive[unit].present); assert (drive[unit].needsMount); assert (drive[unit].mounted == false); if (drive[unit].removable) { MsgLog::fatal("Removable unit locking not yet implemented\n"); } drive[unit].mounted = true; drive[unit].needsMount = false; totalMountedUnits++;}voidide_hwif::GetUnitInfo(uint8_t unit, BlockUnitInfo& ui){ assert (unit < MAX_DRIVE); if (drive[unit].present == false) { ui.isDisk = false; } else { ui.b_geom = drive[unit].b_chs; ui.d_geom = drive[unit].l_chs; ui.nSecs = drive[unit].Capacity(); ui.isEros = true; /* if any partition might be EROS */ ui.isDisk = (drive[unit].media == IDE::med_disk) ? true : false; ui.isMounted = drive[unit].mounted; ui.needsMount = drive[unit].needsMount; ui.hasPartitions = true; ui.isBoot = false; /* FIX: If we're not on a DOS machine.... */ if (ioBase == BIOS_HD_BASE && ((unit | 0x80) == unit)) ui.isBoot = true; }}voidide_hwif::InsertRequest(Request *req){#if 0 MsgLog::printf("Request ior=0x%08x inserted on hwif=0x%08x\n", req, this);#endif assert (req->unit < MAX_DRIVE); assert (drive[req->unit].mounted == true); drive[req->unit].rq.InsertRequest(req); StartIO();}/* INTERRUPT HANDLERS: */boolide_hwif::SetMultmodeIntr(){ uint8_t status = Get8(IDE_STATUS); if (OK_STAT(status, READY_STAT, BAD_STAT)) { drive[cur_drive].multCount = drive[cur_drive].multReq; } else { drive[cur_drive].multCount = 0; drive[cur_drive].multReq = 0; drive[cur_drive].flags.b.recal = 1; drive[cur_drive].DumpStatus("Multmode interrupt", status, 0); } #ifdef IDE_DEBUG MsgLog::printf("SetMultMode: xfer in %d sec blocks\n", drive[cur_drive].multCount);#endif return true;}boolide_hwif::RecalibrateIntr(){ uint8_t status = Get8(IDE_STATUS); if (!OK_STAT(status,READY_STAT,BAD_STAT)) drive[cur_drive].Error("recalibrate interrupt", status);#ifdef IDE_DEBUG MsgLog::printf("RecalIntr(): Set int handler to 0\n");#endif int_handler = 0; return true;}boolide_hwif::SetGeometryIntr(){ uint8_t status = Get8(IDE_STATUS); if (!OK_STAT(status,READY_STAT,BAD_STAT)) drive[cur_drive].Error("set geometry intr", status);#ifdef IDE_DEBUG MsgLog::printf("SetGeomIntr(): Set int handler to 0\n");#endif int_handler = 0; return true;}/* When using IDE drives, we always issue a request for the total * desired number of sectors. If the drive supports READ MULTIPLE we * use it to reduce the number of generated interrupts, else we live * with taking one interrupt per sector. */boolide_hwif::ReadIntr(){ uint8_t status = Get8(IDE_STATUS); if (!OK_STAT(status,DATA_READY,BAD_R_STAT)) { drive[cur_drive].Error("read intr", status); return true; } uint32_t max_xfer = drive[cur_drive].multCount; if (max_xfer == 0) max_xfer = 1; /* What follows is really the same code as in MultWrite, and it * isn't clear to me why it is replicated here. */ Request *req = group->curReq; uint32_t nsec = req->nsec; if (nsec > max_xfer) nsec = max_xfer; #ifdef IDE_DEBUG MsgLog::printf("Transfer was 0x%08x: %d sectors at %d nsec %d\n", req->req_ioaddr, req->req_nsec, req->req_start, req->nsec); MsgLog::printf("Transferring %d sectors of data\n", nsec);#endif drive[cur_drive].InputData((void*) req->req_ioaddr, nsec * EROS_SECTOR_SIZE >> 2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -